M docs/doctasks.py => docs/doctasks.py +7 -8
@@ 3,9 3,8 @@ import xml.etree.ElementTree as xml
from datetime import date
from logging import error
from pathlib import Path
-from typing import Set
-import chevron # type: ignore
+import chevron
import yatte
from yatte import task
@@ 62,7 61,7 @@ def upload_docs():
# Helper functions
-def pipe(cmd: str, input=None) -> str:
+def pipe(cmd, input=None):
"""Run a shell command and return its stdout output.
If `input` is not None, pipes in the text via stdin.
@@ 75,24 74,24 @@ def pipe(cmd: str, input=None) -> str:
return p.stdout
-def scdoc2man(scd: Path, man: Path):
+def scdoc2man(scd, man):
"""Convert manual in scdoc format to man format."""
stderr(f"$ chevron {scd} | scdoc > {man}")
scdoc = chevron.render(scd.read_text(), {"version": yatte.__version__})
pipe(f"scdoc > {man}", input=scdoc)
-def cp(src: Path, dest: Path):
+def cp(src, dest):
if not dest.is_file() or is_newer(src, than=dest):
run(f"cp -p {src} {dest}")
-def uptodate(f: Path, deps: Set[Path]) -> bool:
+def uptodate(f, deps):
# like is_newer() but takes multiple files as 2nd arg.
return f.is_file() and all(f.stat().st_mtime > d.stat().st_mtime for d in deps)
-def render_page(page: Path, template: str, out_html: Path):
+def render_page(page, template, out_html):
"""Inject page into template and write to HTML file."""
content = page.read_text()
@@ 108,7 107,7 @@ def render_page(page: Path, template: str, out_html: Path):
out_html.write_text(rendered)
-def get_title(doc: str) -> str:
+def get_title(doc):
"""Return body of first h1 element in an HTML document.
The doc must be a well-formed XML snippet:
M requirements.txt => requirements.txt +0 -1
@@ 3,6 3,5 @@ isort~=5.10
flake8~=4.0
flake8-bugbear~=22.1
flit~=3.6
-mypy~=0.931
pytest~=7.0
chevron~=0.14
M tasks.py => tasks.py +3 -11
@@ 20,12 20,6 @@ def install_dependencies():
check_installed("scdoc")
-@task("typecheck")
-def check_types():
- """Run type checker."""
- run("mypy .")
-
-
@task("lint")
def run_linters():
"""Run linters."""
@@ 45,12 39,11 @@ def run_tests():
@task("check")
def check():
- """lint + typecheck + test"""
+ """lint + test"""
cmds = [
"isort --check .",
"black --check .",
"flake8 .",
- "mypy .",
"pytest -q .",
]
runp(cmds)
@@ 64,7 57,7 @@ def format():
sys.path.insert(0, "docs")
-import doctasks # type: ignore # noqa: E402 F401
+import doctasks # noqa: E402 F401
@task("upload")
@@ 76,7 69,6 @@ def pypi():
@task("clean")
def clean():
"""Remove build/test artefacts."""
- run("rm -rf .mypy_cache")
run("rm -rf .pytest_cache")
run("rm -rf dist")
run("rm -rf docs/_built")
@@ 85,6 77,6 @@ def clean():
# Helper functions
-def check_installed(cmd: str):
+def check_installed(cmd):
if shutil.which(cmd) is None:
warning("%r is required for some tasks but is not installed.", cmd)
M yatte/cli.py => yatte/cli.py +1 -1
@@ 16,7 16,7 @@ SUCCESS_MSG_ENVVAR = "YATTE_SUCCESS_MSG"
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
-def parse_args() -> Namespace:
+def parse_args():
"""Sets up the CLI and returns the arguments provided by the user."""
ns = Namespace()
ns.task_file = getenv(TASKFILE_ENVVAR, "tasks.py")
D yatte/py.typed => yatte/py.typed +0 -0
M yatte/taskfile.py => yatte/taskfile.py +2 -2
@@ 13,7 13,7 @@ class TaskfileImportError(ImportError):
pass
-def load_taskfile(path: str):
+def load_taskfile(path):
"""Load the tasks from the given file.
Nothing is returned but the tasks are registered on the `Task` class
@@ 41,7 41,7 @@ def load_taskfile(path: str):
raise TaskfileImportError(f"Failed to import {path!r} as a Python module")
module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module) # type: ignore
+ spec.loader.exec_module(module)
# Note: The module doesn't need to be added to `sys.modules`,
# since it won't actually be imported by name / import path.
M yatte/tasklist.py => yatte/tasklist.py +11 -12
@@ 2,7 2,6 @@
from __future__ import annotations
from inspect import getdoc, getfile, signature
-from typing import Callable, Dict
from .taskfile import load_taskfile
@@ 14,9 13,9 @@ class Task:
the task name and the first line of the function docstring.
"""
- _instances: list[Task] = []
+ _instances = []
- def __init__(self, name: str, fn: Callable):
+ def __init__(self, name, fn):
self.name = name
self.fn = fn
@@ 33,21 32,21 @@ class Task:
self.fn(*args)
- def __repr__(self) -> str:
+ def __repr__(self):
return f"Task({self.name!r}: <{getfile(self.fn)}>:{self.fn.__qualname__})"
- def __str__(self) -> str:
+ def __str__(self):
arglist = " ".join(self.args)
signature = f"{self.name} {arglist}"
return f"{signature:<30} {self.doc}"
@property
- def args(self) -> tuple[str, ...]:
+ def args(self):
"""The names of the function parameters"""
return tuple(signature(self.fn).parameters)
@property
- def doc(self) -> str:
+ def doc(self):
"""The first line of the function docstring"""
docstring = getdoc(self.fn) or ""
return docstring and docstring.splitlines()[0]
@@ 59,7 58,7 @@ class ArgCountError(TypeError):
pass
-class TaskList(Dict[str, Task]):
+class TaskList(dict):
"""A mapping of Tasks indexed on task name
When instantiating this class,
@@ 69,21 68,21 @@ class TaskList(Dict[str, Task]):
def __init__(self):
super().__init__({t.name: t for t in Task._instances})
- def __str__(self) -> str:
+ def __str__(self):
return "\n".join(map(str, self.values())) or "<No tasks defined>"
@classmethod
- def load_from(cls, task_file: str) -> TaskList:
+ def load_from(cls, task_file):
"""Load Tasks defined in task_file into a TaskList."""
# Import task_file, registering Tasks in Task._instances.
load_taskfile(task_file)
return TaskList()
-def task(name: str) -> Callable:
+def task(name):
"""A decorator for turning functions into Tasks"""
- def make_task(fn: Callable) -> Task:
+ def make_task(fn):
return Task(name, fn)
return make_task
M yatte/utils.py => yatte/utils.py +8 -10
@@ 4,17 4,15 @@ import subprocess
import sys
from concurrent.futures import ProcessPoolExecutor
from logging import error
-from pathlib import Path
from shlex import quote
-from typing import Iterable, List, Union
-def stderr(s: str):
+def stderr(s):
"""Print a string to stderr."""
print(s, file=sys.stderr)
-def run(cmd: Union[str, List[str]]):
+def run(cmd):
"""Run a shell command."""
if isinstance(cmd, list):
cmd = " ".join(map(quote, cmd))
@@ 27,7 25,7 @@ def run(cmd: Union[str, List[str]]):
raise SystemExit(e.returncode)
-def runp(cmds: Iterable[str]):
+def runp(cmds):
"""Run shell commands in parallel."""
with ProcessPoolExecutor() as executor:
retcodes = list(executor.map(_run, cmds))
@@ 36,7 34,7 @@ def runp(cmds: Iterable[str]):
raise SystemExit(max(retcodes))
-def _run(cmd: str) -> int:
+def _run(cmd):
"""Run a shell command and print its output upon completion."""
p = subprocess.run(cmd, shell=True, capture_output=True, text=True)
stderr(f"$ {cmd}")
@@ 53,24 51,24 @@ def _run(cmd: str) -> int:
return p.returncode
-def mkdir(d: Path):
+def mkdir(d):
"""Create directory d if it doesn't already exist."""
if not d.is_dir():
run(["mkdir", "-p", str(d)])
-def cp(src: Path, dest: Path):
+def cp(src, dest):
"""Copy src to dest if dest doesn't already exist."""
if not dest.is_file():
mkdir(dest.parent)
run(["cp", "-p", str(src), str(dest)])
-def is_newer(f: Path, than: Path) -> bool:
+def is_newer(f, than):
"""Return True if f exists and is newer than the second argument."""
return f.is_file() and f.stat().st_mtime > than.stat().st_mtime
-def check_envvars(names: set) -> set:
+def check_envvars(names):
"""Return the environment variables in names that are undefined."""
return names - set(os.environ)