Implement mypy fixes across all python files (#4)

• Type hinting added to every single file
• Path conversion now handled in `ninja_syntax.py` natively, can safely pass almost everything directly
This commit is contained in:
Thaddeus Crews
2024-01-23 23:21:46 -07:00
committed by GitHub
parent 04c8b45f93
commit 575e3b4a46
9 changed files with 340 additions and 312 deletions
+1
View File
@@ -2,6 +2,7 @@ __pycache__
.idea
.vscode
.ninja_*
.mypy_cache
*.exe
build
build.ninja
+2 -1
View File
@@ -21,6 +21,7 @@
"build/**/*.MAP": true,
"build.ninja": true,
".ninja_*": true,
"objdiff.json": true
"objdiff.json": true,
".mypy_cache": true
}
}
+3 -2
View File
@@ -16,6 +16,7 @@ import sys
import argparse
from pathlib import Path
from typing import Dict, List, Any
from tools.project import (
Object,
ProjectConfig,
@@ -183,7 +184,7 @@ config.linker_version = "GC/1.3.2"
# Helper function for Dolphin libraries
def DolphinLib(lib_name, objects):
def DolphinLib(lib_name: str, objects: List[Object]) -> Dict[str, Any]:
return {
"lib": lib_name,
"mw_version": "GC/1.2.5n",
@@ -194,7 +195,7 @@ def DolphinLib(lib_name, objects):
# Helper function for REL script objects
def Rel(lib_name, objects):
def Rel(lib_name: str, objects: List[Object]) -> Dict[str, Any]:
return {
"lib": lib_name,
"mw_version": "GC/1.3.2",
+14 -9
View File
@@ -13,6 +13,7 @@
import argparse
import os
import re
from typing import List, Set
script_dir = os.path.dirname(os.path.realpath(__file__))
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
@@ -20,9 +21,10 @@ src_dir = os.path.join(root_dir, "src")
include_dir = os.path.join(root_dir, "include")
include_pattern = re.compile(r'^#include\s*[<"](.+?)[>"]$')
guard_pattern = re.compile(r'^#ifndef\s+(.*)$')
guard_pattern = re.compile(r"^#ifndef\s+(.*)$")
defines: Set[str] = set()
defines = set()
def import_h_file(in_file: str, r_path: str) -> str:
rel_path = os.path.join(root_dir, r_path, in_file)
@@ -35,9 +37,10 @@ def import_h_file(in_file: str, r_path: str) -> str:
print("Failed to locate", in_file)
exit(1)
def import_c_file(in_file) -> str:
def import_c_file(in_file: str) -> str:
in_file = os.path.relpath(in_file, root_dir)
out_text = ''
out_text = ""
try:
with open(in_file, encoding="utf-8") as file:
@@ -47,8 +50,9 @@ def import_c_file(in_file) -> str:
out_text += process_file(in_file, list(file))
return out_text
def process_file(in_file: str, lines) -> str:
out_text = ''
def process_file(in_file: str, lines: List[str]) -> str:
out_text = ""
for idx, line in enumerate(lines):
guard_match = guard_pattern.match(line.strip())
if idx == 0:
@@ -59,15 +63,16 @@ def process_file(in_file: str, lines) -> str:
print("Processing file", in_file)
include_match = include_pattern.match(line.strip())
if include_match and not include_match[1].endswith(".s"):
out_text += f"/* \"{in_file}\" line {idx} \"{include_match[1]}\" */\n"
out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n'
out_text += import_h_file(include_match[1], os.path.dirname(in_file))
out_text += f"/* end \"{include_match[1]}\" */\n"
out_text += f'/* end "{include_match[1]}" */\n'
else:
out_text += line
return out_text
def main():
def main() -> None:
parser = argparse.ArgumentParser(
description="""Create a context file which can be used for decomp.me"""
)
+7 -7
View File
@@ -18,11 +18,11 @@ import shutil
import stat
import urllib.request
import zipfile
from typing import Callable, Dict
from pathlib import Path
def dtk_url(tag):
def dtk_url(tag: str) -> str:
uname = platform.uname()
suffix = ""
system = uname.system.lower()
@@ -38,21 +38,21 @@ def dtk_url(tag):
return f"{repo}/releases/download/{tag}/dtk-{system}-{arch}{suffix}"
def sjiswrap_url(tag):
def sjiswrap_url(tag: str) -> str:
repo = "https://github.com/encounter/sjiswrap"
return f"{repo}/releases/download/{tag}/sjiswrap-windows-x86.exe"
def wibo_url(tag):
def wibo_url(tag: str) -> str:
repo = "https://github.com/decompals/wibo"
return f"{repo}/releases/download/{tag}/wibo"
def compilers_url(tag):
def compilers_url(tag: str) -> str:
return f"https://files.decomp.dev/compilers_{tag}.zip"
TOOLS = {
TOOLS: Dict[str, Callable[[str], str]] = {
"dtk": dtk_url,
"sjiswrap": sjiswrap_url,
"wibo": wibo_url,
@@ -60,7 +60,7 @@ TOOLS = {
}
def main():
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("tool", help="Tool name")
parser.add_argument("output", type=Path, help="output file path")
+79 -53
View File
@@ -21,50 +21,67 @@ use Python.
import re
import textwrap
from typing import Optional, Union, Tuple, Match, Dict, List
from io import StringIO
from pathlib import Path
def escape_path(word):
NinjaPath = Union[str, Path]
NinjaPaths = Union[
List[str],
List[Path],
List[NinjaPath],
List[Optional[str]],
List[Optional[Path]],
List[Optional[NinjaPath]],
]
NinjaPathOrPaths = Union[NinjaPath, NinjaPaths]
def escape_path(word: str) -> str:
return word.replace("$ ", "$$ ").replace(" ", "$ ").replace(":", "$:")
class Writer(object):
def __init__(self, output, width=78):
def __init__(self, output: StringIO, width: int = 78) -> None:
self.output = output
self.width = width
def newline(self):
def newline(self) -> None:
self.output.write("\n")
def comment(self, text):
def comment(self, text: str) -> None:
for line in textwrap.wrap(
text, self.width - 2, break_long_words=False, break_on_hyphens=False
):
self.output.write("# " + line + "\n")
def variable(self, key, value, indent=0):
if value is None:
return
if isinstance(value, list):
value = " ".join(filter(None, value)) # Filter out empty strings.
def variable(
self,
key: str,
value: Optional[NinjaPathOrPaths],
indent: int = 0,
) -> None:
value = " ".join(serialize_paths(value))
self._line("%s = %s" % (key, value), indent)
def pool(self, name, depth):
def pool(self, name: str, depth: int) -> None:
self._line("pool %s" % name)
self.variable("depth", depth, indent=1)
self.variable("depth", str(depth), indent=1)
def rule(
self,
name,
command,
description=None,
depfile=None,
generator=False,
pool=None,
restat=False,
rspfile=None,
rspfile_content=None,
deps=None,
):
name: str,
command: str,
description: Optional[str] = None,
depfile: Optional[NinjaPath] = None,
generator: bool = False,
pool: Optional[str] = None,
restat: bool = False,
rspfile: Optional[NinjaPath] = None,
rspfile_content: Optional[NinjaPath] = None,
deps: Optional[NinjaPathOrPaths] = None,
) -> None:
self._line("rule %s" % name)
self.variable("command", command, indent=1)
if description:
@@ -86,30 +103,37 @@ class Writer(object):
def build(
self,
outputs,
rule,
inputs=None,
implicit=None,
order_only=None,
variables=None,
implicit_outputs=None,
pool=None,
dyndep=None,
):
outputs = as_list(outputs)
outputs: NinjaPathOrPaths,
rule: str,
inputs: Optional[NinjaPathOrPaths] = None,
implicit: Optional[NinjaPathOrPaths] = None,
order_only: Optional[NinjaPathOrPaths] = None,
variables: Optional[
Union[
List[Tuple[str, Optional[NinjaPathOrPaths]]],
Dict[str, Optional[NinjaPathOrPaths]],
]
] = None,
implicit_outputs: Optional[NinjaPathOrPaths] = None,
pool: Optional[str] = None,
dyndep: Optional[NinjaPath] = None,
) -> List[str]:
outputs = serialize_paths(outputs)
out_outputs = [escape_path(x) for x in outputs]
all_inputs = [escape_path(x) for x in as_list(inputs)]
all_inputs = [escape_path(x) for x in serialize_paths(inputs)]
if implicit:
implicit = [escape_path(x) for x in as_list(implicit)]
implicit = [escape_path(x) for x in serialize_paths(implicit)]
all_inputs.append("|")
all_inputs.extend(implicit)
if order_only:
order_only = [escape_path(x) for x in as_list(order_only)]
order_only = [escape_path(x) for x in serialize_paths(order_only)]
all_inputs.append("||")
all_inputs.extend(order_only)
if implicit_outputs:
implicit_outputs = [escape_path(x) for x in as_list(implicit_outputs)]
implicit_outputs = [
escape_path(x) for x in serialize_paths(implicit_outputs)
]
out_outputs.append("|")
out_outputs.extend(implicit_outputs)
@@ -119,7 +143,7 @@ class Writer(object):
if pool is not None:
self._line(" pool = %s" % pool)
if dyndep is not None:
self._line(" dyndep = %s" % dyndep)
self._line(" dyndep = %s" % serialize_path(dyndep))
if variables:
if isinstance(variables, dict):
@@ -132,16 +156,16 @@ class Writer(object):
return outputs
def include(self, path):
def include(self, path: str) -> None:
self._line("include %s" % path)
def subninja(self, path):
def subninja(self, path: str) -> None:
self._line("subninja %s" % path)
def default(self, paths):
self._line("default %s" % " ".join(as_list(paths)))
def default(self, paths: NinjaPathOrPaths) -> None:
self._line("default %s" % " ".join(serialize_paths(paths)))
def _count_dollars_before_index(self, s, i):
def _count_dollars_before_index(self, s: str, i: int) -> int:
"""Returns the number of '$' characters right in front of s[i]."""
dollar_count = 0
dollar_index = i - 1
@@ -150,7 +174,7 @@ class Writer(object):
dollar_index -= 1
return dollar_count
def _line(self, text, indent=0):
def _line(self, text: str, indent: int = 0) -> None:
"""Write 'text' word-wrapped at self.width characters."""
leading_space = " " * indent
while len(leading_space) + len(text) > self.width:
@@ -187,19 +211,21 @@ class Writer(object):
self.output.write(leading_space + text + "\n")
def close(self):
def close(self) -> None:
self.output.close()
def as_list(input):
if input is None:
return []
def serialize_path(input: Optional[NinjaPath]) -> str:
return str(input).replace("\\", "/") if input else ""
def serialize_paths(input: Optional[NinjaPathOrPaths]) -> List[str]:
if isinstance(input, list):
return input
return [input]
return [serialize_path(path) for path in input if path]
return [serialize_path(input)] if input else []
def escape(string):
def escape(string: str) -> str:
"""Escape a string such that it can be embedded into a Ninja file without
further interpretation."""
assert "\n" not in string, "Ninja syntax does not allow newlines"
@@ -207,14 +233,14 @@ def escape(string):
return string.replace("$", "$$")
def expand(string, vars, local_vars={}):
def expand(string: str, vars: Dict[str, str], local_vars: Dict[str, str] = {}) -> str:
"""Expand a string containing $vars as Ninja would.
Note: doesn't handle the full Ninja variable syntax, but it's enough
to make configure.py's use of it work.
"""
def exp(m):
def exp(m: Match[str]) -> str:
var = m.group(1)
if var == "$":
return "$"
+197 -206
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -25,7 +25,7 @@ def in_wsl() -> bool:
return "microsoft-standard" in uname().release
def import_d_file(in_file) -> str:
def import_d_file(in_file: str) -> str:
out_text = ""
with open(in_file) as file:
@@ -60,7 +60,7 @@ def import_d_file(in_file) -> str:
return out_text
def main():
def main() -> None:
parser = argparse.ArgumentParser(
description="""Transform a .d file from Wine paths to normal paths"""
)
+6 -3
View File
@@ -51,7 +51,7 @@ if __name__ == "__main__":
args = parser.parse_args()
api_key = args.api_key or os.environ.get("PROGRESS_API_KEY")
if not api_key:
raise "API key required"
raise KeyError("API key required")
url = generate_url(args)
entries = []
@@ -68,9 +68,12 @@ if __name__ == "__main__":
print("Publishing entry to", url)
json.dump(entries[0], sys.stdout, indent=4)
print()
r = requests.post(url, json={
r = requests.post(
url,
json={
"api_key": api_key,
"entries": entries,
})
},
)
r.raise_for_status()
print("Done!")