Skip to content

Commit

Permalink
ci: add type checking with Pyright
Browse files Browse the repository at this point in the history
  • Loading branch information
ahal committed Nov 15, 2023
1 parent f45f3c8 commit fe20154
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 21 deletions.
20 changes: 19 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ tox = "^4.6.4"
coverage = {extras = ["toml"], version = "^7.2.7"}
requests = "^2.31.0"


[tool.poetry.group.type.dependencies]
pyright = "^1.1.336"

[tool.black]
extend-exclude = """(\
reps/templates)\
Expand All @@ -39,6 +43,11 @@ testpaths = ["test"]
[tool.ruff]
exclude = ["reps/templates"]

[tool.pyright]
include = ["reps"]
ignore = ["reps/templates"]
reportUnknownParameterType = "error"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 5 additions & 4 deletions reps/console.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
from argparse import ArgumentParser
from pathlib import Path
from typing import Any, List

from cookiecutter.main import cookiecutter

Expand All @@ -12,7 +13,7 @@ def available_templates():
return [path.name for path in TEMPLATE_DIR.iterdir() if path.is_dir()]


def command_new(name, template, **cookiecutter_args):
def command_new(name: str, template: str, **cookiecutter_args: Any):
if template not in available_templates():
print(f"template '{template}' not found!")
return 1
Expand All @@ -23,17 +24,17 @@ def command_new(name, template, **cookiecutter_args):
# specified, ensure we don't error out when the project already exists.
cookiecutter_args.setdefault("overwrite_if_exists", False)

template = TEMPLATE_DIR / template
template = str(TEMPLATE_DIR / template)
cookiecutter_args.setdefault("extra_context", {}).setdefault("project_name", name)

# Generate the project.
cookiecutter(
str(template),
template,
**cookiecutter_args,
)


def run(args=sys.argv[1:]):
def run(args: List[str] = sys.argv[1:]):
parser = ArgumentParser()
parser.add_argument(
"name", nargs="?", default=None, help="Name of the project to create."
Expand Down
31 changes: 15 additions & 16 deletions reps/hooks.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
import subprocess
from collections import defaultdict
from pathlib import Path
from typing import Any, Callable, Generator, List

from ruamel.yaml import YAML
from halo import Halo

from reps.console import command_new
from reps.types import HookFn, Items


HOOKS = defaultdict(list)


def hook(name):
def wrapper(func):
def hook(name: str) -> Callable[[HookFn], HookFn]:
def wrapper(func: HookFn) -> HookFn:
HOOKS[name].append(func)
return func

return wrapper


def run_hooks(group, items):
def run_hooks(group: str, items: Items):
for hook_fn in HOOKS[group]:
with Halo(f"running {hook_fn.__name__}") as spinner:
hook_fn(items)
spinner.succeed(f"{hook_fn.__name__}")


def run(cmd, **kwargs):
def run(cmd: List[str], **kwargs: Any):
kwargs.setdefault("check", True)
kwargs.setdefault("text", True)
kwargs.setdefault("stdout", subprocess.PIPE)
Expand All @@ -40,7 +42,7 @@ def run(cmd, **kwargs):


@hook("pre-gen-py")
def base_init(items):
def base_init(items: Items):
"""Generate the 'base' template first."""
if "_copy_without_render" in items:
del items["_copy_without_render"]
Expand All @@ -57,7 +59,7 @@ def base_init(items):


@hook("post-gen-py")
def merge_pre_commit(items):
def merge_pre_commit(items: Items):
"""Update the base pre-commit config with Python-specific tools."""

yaml = YAML()
Expand Down Expand Up @@ -88,15 +90,15 @@ def merge_pre_commit(items):


@hook("post-gen-py")
def add_poetry_dependencies(items):
def add_poetry_dependencies(items: Items):
# Build constraints to ensure we don't try to add versions
# that are incompatible with the minimum Python.
min_python = items["min_python_version"]
constraints = defaultdict(dict)
constraints["coverage"] = {"3.7": "coverage@<7.3.0"}
constraints["tox"] = {"3.7": "tox@<4.9.0"}

def build_specifiers(*packages):
def build_specifiers(*packages: str) -> Generator[str, None, None]:
for p in packages:
yield constraints[p].get(min_python, p)

Expand All @@ -111,15 +113,12 @@ def build_specifiers(*packages):
+ list(build_specifiers("sphinx<7", "sphinx-autobuild", "sphinx-book-theme"))
)

run(
["poetry", "add", "--group=type"]
+ list(build_specifiers("pyright"))
)
run(["poetry", "add", "--group=type"] + list(build_specifiers("pyright")))


@hook("post-gen-py")
@hook("post-gen-base")
def git_init(items):
def git_init(items: Items):
run(["git", "init"])
run(["git", "checkout", "-b", "main"])
run(
Expand All @@ -135,16 +134,16 @@ def git_init(items):

@hook("post-gen-py")
@hook("post-gen-base")
def pre_commit_autoupdate(items):
def pre_commit_autoupdate(items: Items):
run(["pre-commit", "autoupdate"])


@hook("post-gen-py")
@hook("post-gen-base")
def lock_taskgraph_requirements(items):
def lock_taskgraph_requirements(items: Items):
run(["pip-compile", "requirements.in", "--generate-hashes"], cwd="taskcluster")


@hook("post-gen-base")
def taskgraph_init(items):
def taskgraph_init(items: Items):
run(["taskgraph", "init"])
5 changes: 5 additions & 0 deletions reps/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from typing import Any, Callable, Dict


Items = Dict[str, Any]
HookFn = Callable[[Items], None]
9 changes: 9 additions & 0 deletions taskcluster/ci/test/kind.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@ tasks:
command: >-
poetry install --only test &&
poetry run tox --parallel
type-check:
description: "Run pyright type checking against code base"
worker:
max-run-time: 300
run:
command: >-
poetry install --only main --only type &&
poetry run pyright

0 comments on commit fe20154

Please sign in to comment.