test: Fix mypy strict errors in unit tests
The unit test files below test/unit/python/jw/pkg/lib/ define Cmd and App subclasses with unannotated methods: __init__(), add_arguments(), _run() and close() lack signatures, the made class attribute lacks a type, and the _cleanup() and exit_context() helpers are untyped, so a strict mypy run over the test tree fails on them. Likewise, the ExecApp and version tests carry list formatting that yapf rejects. Annotate the test classes following the library conventions: parent is App | Cmd, the parser is ArgumentParser, and args is Namespace, and mark every overridden method with @override. Guard the imports that are only used for annotations behind TYPE_CHECKING, and add the future annotations import so they stay out of the runtime path. Then reformat the test tree with yapf. That folds the opts list of the ExecApp test and re-indents the rejected operator list of the version test. Signed-off-by: Jan Lindemann <jan@janware.com> Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.85.1
This commit is contained in:
parent
432b07060b
commit
64f1a73650
4 changed files with 46 additions and 27 deletions
|
|
@ -1,10 +1,16 @@
|
|||
import asyncio
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from jw.pkg.lib.App import App
|
||||
from jw.pkg.lib.Cmd import Cmd
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
# -- A minimal command tree: a root command hosting one child command. The
|
||||
# root mimics the "run + opts" unit the App can now mount at the top level:
|
||||
# it adds options, sets up, dispatches to the selected child (or runs its own
|
||||
|
|
@ -12,24 +18,26 @@ from jw.pkg.lib.Cmd import Cmd
|
|||
|
||||
class ChildCmd(Cmd):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: App | Cmd) -> None:
|
||||
super().__init__(parent, 'child', 'A child command')
|
||||
self.ran = False
|
||||
self.got_opt = None
|
||||
|
||||
def add_arguments(self, parser):
|
||||
@override
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument('--child-opt', default = 'child-default')
|
||||
|
||||
async def _run(self, args):
|
||||
@override
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
self.ran = True
|
||||
self.got_opt = args.child_opt
|
||||
|
||||
class RootCmd(Cmd):
|
||||
|
||||
made = []
|
||||
made: list['RootCmd'] = []
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: App | Cmd) -> None:
|
||||
super().__init__(parent, 'root', 'Root command')
|
||||
self.child = ChildCmd(self)
|
||||
self.add_subcommands(self.child)
|
||||
|
|
@ -38,11 +46,13 @@ class RootCmd(Cmd):
|
|||
self.default = False
|
||||
RootCmd.made.append(self)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
@override
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument('--root-opt', default = 'root-default')
|
||||
|
||||
async def _run(self, args):
|
||||
@override
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
self.setup = True
|
||||
try:
|
||||
if hasattr(args, 'func'):
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
import asyncio
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from jw.pkg.lib.Cmd import Cmd
|
||||
from jw.pkg.lib.ExecApp import ExecApp
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from argparse import Namespace
|
||||
|
||||
from jw.pkg.lib.App import App
|
||||
|
||||
# -- A minimal root command so the app can be built without a command tree.
|
||||
|
||||
class RootCmd(Cmd):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: App | Cmd) -> None:
|
||||
super().__init__(parent, 'root', 'Root command')
|
||||
|
||||
async def _run(self, args):
|
||||
@override
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
pass
|
||||
|
||||
# -- Counts the close() calls made by App.__aexit__() when the async context
|
||||
|
|
@ -22,7 +31,8 @@ class RecordingExecApp(ExecApp):
|
|||
|
||||
closed = 0
|
||||
|
||||
def close(self):
|
||||
@override
|
||||
def close(self) -> None:
|
||||
RecordingExecApp.closed += 1
|
||||
super().close()
|
||||
|
||||
|
|
@ -38,16 +48,14 @@ finally:
|
|||
|
||||
# -- ExecApp registers the exec-related options on the top-level parser.
|
||||
|
||||
opts = [
|
||||
o for a in app.parser._actions for o in getattr(a, 'option_strings', ())
|
||||
]
|
||||
opts = [o for a in app.parser._actions for o in getattr(a, 'option_strings', ())]
|
||||
for opt in ('--interactive', '--verbose', '--target'):
|
||||
assert opt in opts, f'{opt} must be registered by ExecApp'
|
||||
|
||||
# -- Exiting the async context must close the app, through the
|
||||
# App.__aexit__() that ExecApp.__aexit__() chains to.
|
||||
|
||||
async def exit_context():
|
||||
async def exit_context() -> None:
|
||||
async with app:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
from jw.pkg.lib.ProjectConf import ProjectConf
|
||||
|
||||
# -- Helper: create temp file and read it --
|
||||
|
|
@ -13,7 +14,7 @@ def _load(text: str) -> ProjectConf:
|
|||
_tmpfiles.append(f.name)
|
||||
return ProjectConf.read(f.name)
|
||||
|
||||
def _cleanup():
|
||||
def _cleanup() -> None:
|
||||
for _p in _tmpfiles:
|
||||
os.unlink(_p)
|
||||
del _p
|
||||
|
|
|
|||
|
|
@ -233,16 +233,16 @@ for bad in ['', 'foo =', ' = 1.0']:
|
|||
# Operators outside the supported set are rejected when parsed,
|
||||
# not folded into the package name or deferred to expansion
|
||||
for bad in [
|
||||
'foo ~= 1.0',
|
||||
'foo~=1.0',
|
||||
'foo ~ 1.0',
|
||||
'foo != 1.0',
|
||||
'foo!=1.0',
|
||||
'foo == 1.0',
|
||||
'foo==1.0',
|
||||
'foo === 1.0',
|
||||
'foo << 1.0',
|
||||
'foo >> 1.0',
|
||||
'foo ~= 1.0',
|
||||
'foo~=1.0',
|
||||
'foo ~ 1.0',
|
||||
'foo != 1.0',
|
||||
'foo!=1.0',
|
||||
'foo == 1.0',
|
||||
'foo==1.0',
|
||||
'foo === 1.0',
|
||||
'foo << 1.0',
|
||||
'foo >> 1.0',
|
||||
]:
|
||||
try:
|
||||
Dependency(bad).full_name
|
||||
|
|
|
|||
Loading…
Reference in a new issue