From 0571618bce62b4f89d7c20638f41830bdea148c7 Mon Sep 17 00:00:00 2001 From: Stanislav Filin Date: Fri, 14 Jul 2023 17:42:00 +0000 Subject: [PATCH] Add type hints, update few functions Signed-off-by: Stanislav Filin --- nox/__main__.py | 38 +++++++++++++++++++++++--------------- nox/_decorators.py | 28 +++++++++++++++++++++------- nox/command.py | 6 ++++-- nox/registry.py | 16 ++++++++-------- nox/tox_to_nox.py | 11 +++++++++-- nox/workflow.py | 3 ++- 6 files changed, 67 insertions(+), 35 deletions(-) diff --git a/nox/__main__.py b/nox/__main__.py index f8dcbfdf..96f51280 100644 --- a/nox/__main__.py +++ b/nox/__main__.py @@ -22,12 +22,34 @@ from __future__ import annotations import sys +from typing import Any from nox import _options, tasks, workflow from nox._version import get_nox_version from nox.logger import setup_logging +def execute_workflow(args: Any) -> int: + """ + Execute the appropriate tasks. + """ + + return workflow.execute( + global_config=args, + workflow=( + tasks.load_nox_module, + tasks.merge_noxfile_options, + tasks.discover_manifest, + tasks.filter_manifest, + tasks.honor_list_request, + tasks.run_manifest, + tasks.print_summary, + tasks.create_report, + tasks.final_reduce, + ), + ) + + def main() -> None: args = _options.options.parse_args() @@ -43,21 +65,7 @@ def main() -> None: color=args.color, verbose=args.verbose, add_timestamp=args.add_timestamp ) - # Execute the appropriate tasks. - exit_code = workflow.execute( - global_config=args, - workflow=( - tasks.load_nox_module, - tasks.merge_noxfile_options, - tasks.discover_manifest, - tasks.filter_manifest, - tasks.honor_list_request, - tasks.run_manifest, - tasks.print_summary, - tasks.create_report, - tasks.final_reduce, - ), - ) + exit_code = execute_workflow(args) # Done; exit. sys.exit(exit_code) diff --git a/nox/_decorators.py b/nox/_decorators.py index 648d96aa..aaef96a9 100644 --- a/nox/_decorators.py +++ b/nox/_decorators.py @@ -26,19 +26,23 @@ if TYPE_CHECKING: from ._parametrize import Param +T = TypeVar("T", bound=Callable[..., Any]) + class FunctionDecorator: + """This is a function decorator.""" + def __new__( - cls, func: Callable[..., Any], *args: Any, **kwargs: Any + cls: Any, func: Callable[..., Any], *args: Any, **kwargs: Any ) -> FunctionDecorator: obj = super().__new__(cls) - return functools.wraps(func)(obj) - - -T = TypeVar("T", bound=Callable[..., Any]) + functools.update_wrapper(obj, func) + return cast(FunctionDecorator, obj) def _copy_func(src: T, name: str | None = None) -> T: + """This function copies another function, optionally with a new name.""" + dst = types.FunctionType( src.__code__, src.__globals__, @@ -53,6 +57,8 @@ def _copy_func(src: T, name: str | None = None) -> T: class Func(FunctionDecorator): + """This is a function decorator that adds additional Nox-specific metadata.""" + def __init__( self, func: Callable[..., Any], @@ -63,7 +69,7 @@ def __init__( venv_params: Any = None, should_warn: Mapping[str, Any] | None = None, tags: Sequence[str] | None = None, - ): + ) -> None: self.func = func self.python = python self.reuse_venv = reuse_venv @@ -77,6 +83,8 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any: return self.func(*args, **kwargs) def copy(self, name: str | None = None) -> Func: + """Copy this function with a new name.""" + return Func( _copy_func(self.func, name), self.python, @@ -90,6 +98,8 @@ def copy(self, name: str | None = None) -> Func: class Call(Func): + """This represents a call of a function with a particular set of arguments.""" + def __init__(self, func: Func, param_spec: Param) -> None: call_spec = param_spec.call_spec session_signature = f"({param_spec})" @@ -124,5 +134,9 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any: return super().__call__(*args, **kwargs) @classmethod - def generate_calls(cls, func: Func, param_specs: Iterable[Param]) -> list[Call]: + def generate_calls( + cls: type[Call], func: Func, param_specs: Iterable[Param] + ) -> list[Call]: + """Generates a list of calls based on the function and parameters.""" + return [cls(func, param_spec) for param_spec in param_specs] diff --git a/nox/command.py b/nox/command.py index 9280f8e6..3215772e 100644 --- a/nox/command.py +++ b/nox/command.py @@ -29,6 +29,8 @@ else: # pragma: no cover from typing import Literal +ExternalType = Literal["error", True, False] + class CommandFailed(Exception): """Raised when an executed command returns a non-success status code.""" @@ -53,7 +55,7 @@ def which(program: str | os.PathLike[str], paths: Sequence[str] | None) -> str: raise CommandFailed(f"Program {program} not found") -def _clean_env(env: Mapping[str, str] | None) -> dict[str, str] | None: +def _clean_env(env: Mapping[str, str] | None = None) -> dict[str, str] | None: if env is None: return None @@ -80,7 +82,7 @@ def run( paths: Sequence[str] | None = None, success_codes: Iterable[int] | None = None, log: bool = True, - external: Literal["error"] | bool = False, + external: ExternalType = False, **popen_kws: Any, ) -> str | bool: """Run a command-line program.""" diff --git a/nox/registry.py b/nox/registry.py index 2f6e5990..60765ae3 100644 --- a/nox/registry.py +++ b/nox/registry.py @@ -36,12 +36,12 @@ def session_decorator(__func: F) -> F: @overload def session_decorator( __func: None = ..., - python: Python = ..., - py: Python = ..., + python: Python | None = ..., + py: Python | None = ..., reuse_venv: bool | None = ..., name: str | None = ..., - venv_backend: Any = ..., - venv_params: Any = ..., + venv_backend: Any | None = ..., + venv_params: Any | None = ..., tags: Sequence[str] | None = ..., ) -> Callable[[F], F]: ... @@ -49,12 +49,12 @@ def session_decorator( def session_decorator( func: F | None = None, - python: Python = None, - py: Python = None, + python: Python | None = None, + py: Python | None = None, reuse_venv: bool | None = None, name: str | None = None, - venv_backend: Any = None, - venv_params: Any = None, + venv_backend: Any | None = None, + venv_params: Any | None = None, tags: Sequence[str] | None = None, ) -> F | Callable[[F], F]: """Designate the decorated function as a session.""" diff --git a/nox/tox_to_nox.py b/nox/tox_to_nox.py index c68b13a5..52a322f6 100644 --- a/nox/tox_to_nox.py +++ b/nox/tox_to_nox.py @@ -31,10 +31,12 @@ def wrapjoin(seq: Iterator[Any]) -> str: + """Wrap each item in single quotes and join them with a comma.""" return ", ".join([f"'{item}'" for item in seq]) def fixname(envname: str) -> str: + """Replace dashes with underscores and check if the result is a valid identifier.""" envname = envname.replace("-", "_") if not envname.isidentifier(): print( @@ -44,6 +46,12 @@ def fixname(envname: str) -> str: return envname +def write_output_to_file(output: str, filename: str) -> None: + """Write output to a file.""" + with open(filename, "w") as outfile: + outfile.write(output) + + def main() -> None: parser = argparse.ArgumentParser(description="Converts toxfiles to noxfiles.") parser.add_argument("--output", default="noxfile.py") @@ -53,5 +61,4 @@ def main() -> None: config = tox.config.parseconfig([]) output = _TEMPLATE.render(config=config, wrapjoin=wrapjoin, fixname=fixname) - with open(args.output, "w") as outfile: - outfile.write(output) + write_output_to_file(output, args.output) diff --git a/nox/workflow.py b/nox/workflow.py index c50bfbce..3b653257 100644 --- a/nox/workflow.py +++ b/nox/workflow.py @@ -49,7 +49,8 @@ def execute( args: list[Any] = [] if return_value is not None: args.append(return_value) - return_value = function_(*args, global_config=global_config) + kwargs: dict[str, Any] = {"global_config": global_config} + return_value = function_(*args, **kwargs) # If we got an integer value as a result, abort task processing # and return it.