diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3343606..d267021 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,3 +40,21 @@ repos: files: "^tests/" args: - --disable=missing-docstring,consider-using-f-string,duplicate-code + - repo: local + # We do not use pre-commit/mirrors-mypy, + # as it comes with opinionated defaults + # (like --ignore-missing-imports) + # and is difficult to configure to run + # with the dependencies correctly installed. + hooks: + - id: mypy + name: mypy + entry: "python3 etc/run_mypy.py" + language: python + additional_dependencies: ["mypy==0.790", "tomlkit"] + types: [python] + # use require_serial so that script + # is only called once per commit + require_serial: true + # Print the number of files as a sanity-check + verbose: true diff --git a/adafruit_pioasm.py b/adafruit_pioasm.py index a6a4ebf..9c1a433 100644 --- a/adafruit_pioasm.py +++ b/adafruit_pioasm.py @@ -14,6 +14,11 @@ import array import re +try: + from typing import List, Tuple, Optional +except: # pylint: disable=bare-except + pass + splitter = re.compile(r",\s*|\s+(?:,\s*)?").split mov_splitter = re.compile("!|~|::").split @@ -40,14 +45,16 @@ class Program: # pylint: disable=too-few-public-methods """ - def __init__(self, text_program: str, *, build_debuginfo=False) -> None: + debuginfo: Optional[Tuple[List[int], str]] + + def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None: """Converts pioasm text to encoded instruction bytes""" # pylint: disable=too-many-branches,too-many-statements,too-many-locals - assembled = [] + assembled: List[int] = [] program_name = None labels = {} linemap = [] - instructions = [] + instructions: List[str] = [] sideset_count = 0 sideset_enable = 0 wrap = None @@ -83,9 +90,9 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 assembled = [] - for instruction in instructions: + for instruction_str in instructions: # print(instruction) - instruction = splitter(instruction.strip()) + instruction = splitter(instruction_str.strip()) delay = 0 if instruction[-1].endswith("]"): # Delay delay = int(instruction[-1].strip("[]"), 0) @@ -242,14 +249,14 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None: else: self.debuginfo = None - def print_c_program(self, name, qualifier="const"): + def print_c_program(self, name: str, qualifier: str = "const") -> None: """Print the program into a C program snippet""" - if self.debuginfo is None: - linemap = None - program_lines = None - else: + if self.debuginfo: linemap = self.debuginfo[0][:] # Use a copy since we destroy it program_lines = self.debuginfo[1].split("\n") + else: + linemap = [] + program_lines = [] print( f"{qualifier} int {name}_wrap = {self.pio_kwargs.get('wrap', len(self.assembled)-1)};" @@ -290,7 +297,7 @@ def print_c_program(self, name, qualifier="const"): print() -def assemble(program_text: str) -> array.array: +def assemble(program_text: str) -> "array.array[int]": """Converts pioasm text to encoded instruction bytes In new code, prefer to use the `Program` class so that the extra arguments diff --git a/etc/run_mypy.py b/etc/run_mypy.py new file mode 100755 index 0000000..f7a9c83 --- /dev/null +++ b/etc/run_mypy.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense +"""Automatically run mypy. Use from pre-commit""" +import os +import pathlib +import subprocess +import tomlkit + + +def print_check_call(command): + """Keep the user aware of commands being executed""" + print("# Running", " ".join(command)) + subprocess.check_call(command) + + +os.chdir(pathlib.Path(__file__).parent.parent) + +pip_command = ["pip", "install", "--no-input", "--quiet", "--editable", "."] +print_check_call(pip_command) + +with open("pyproject.toml") as f: + meta = tomlkit.load(f) +mypy_command = ["mypy"] +if meta["tool"].get("adafruit", {}).get("mypy-strict", True): + mypy_command.append("--strict") +for module in meta["tool"]["setuptools"].get("py-modules", []): + mypy_command.extend(["-m", module]) +for module in meta["tool"]["setuptools"].get("packages", []): + mypy_command.extend(["-p", module]) + +print_check_call(mypy_command)