From 65016e582fe25d4c611cd9d05c58f9637f62f41d Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 15:59:26 -0500 Subject: [PATCH 1/8] test: update mypy typings --- dev-requirements.txt | 2 +- invoke/collection.py | 6 +++--- invoke/parser/context.py | 2 +- invoke/runners.py | 22 ++++++++++++++++++++-- invoke/tasks.py | 5 +++-- invoke/util.py | 4 ++-- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 1bf0ad73..c00b0667 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -19,5 +19,5 @@ setuptools>56 # Debuggery icecream>=2.1 # typing -mypy==0.971 +mypy==1.4.1 types-PyYAML==6.0.12.4 diff --git a/invoke/collection.py b/invoke/collection.py index 23dcff92..9c654ca5 100644 --- a/invoke/collection.py +++ b/invoke/collection.py @@ -1,6 +1,6 @@ import copy from types import ModuleType -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Optional, Tuple, Union from .util import Lexicon, helpline @@ -266,7 +266,7 @@ def add_task( name = task.name # XXX https://github.com/python/mypy/issues/1424 elif hasattr(task.body, "func_name"): - name = task.body.func_name # type: ignore + name = task.body.func_name elif hasattr(task.body, "__name__"): name = task.__name__ else: @@ -284,7 +284,7 @@ def add_task( def add_collection( self, - coll: "Collection", + coll: Union["Collection", ModuleType], name: Optional[str] = None, default: Optional[bool] = None, ) -> None: diff --git a/invoke/parser/context.py b/invoke/parser/context.py index 359e9f9e..1d44712c 100644 --- a/invoke/parser/context.py +++ b/invoke/parser/context.py @@ -91,7 +91,7 @@ def __init__( self.args = Lexicon() self.positional_args: List[Argument] = [] self.flags = Lexicon() - self.inverse_flags: Dict[str, str] = {} # No need for Lexicon here + self.inverse_flags: Dict[str, Any] = {} # No need for Lexicon here self.name = name self.aliases = aliases for arg in args: diff --git a/invoke/runners.py b/invoke/runners.py index f1c888f4..a67baa30 100644 --- a/invoke/runners.py +++ b/invoke/runners.py @@ -19,6 +19,7 @@ Optional, Tuple, Type, + overload, ) # Import some platform-specific things at top level so they can be mocked for @@ -122,7 +123,19 @@ def __init__(self, context: "Context") -> None: self._asynchronous = False self._disowned = False - def run(self, command: str, **kwargs: Any) -> Optional["Result"]: + @overload + def run( + self, command: str, *, disowned: None, **kwargs: Any + ) -> "Result": + ... + + @overload + def run(self, command: str, *, disowned: bool, **kwargs: Any) -> None: + ... + + def run( + self, command: str, *, disowned: Optional[bool] = None, **kwargs: Any + ) -> Optional["Result"]: """ Execute ``command``, returning an instance of `Result` once complete. @@ -391,6 +404,8 @@ def run(self, command: str, **kwargs: Any) -> Optional["Result"]: .. versionadded:: 1.0 """ + if disowned is not None: + kwargs['disowned'] = disowned try: return self._run_body(command, **kwargs) finally: @@ -1481,7 +1496,7 @@ def __init__( exited: int = 0, pty: bool = False, hide: Tuple[str, ...] = tuple(), - ): + ) -> None: self.stdout = stdout self.stderr = stderr if encoding is None: @@ -1506,6 +1521,9 @@ def return_code(self) -> int: def __bool__(self) -> bool: return self.ok + def __int__(self) -> int: + return self.exited + def __str__(self) -> str: if self.exited is not None: desc = "Command exited with status {}.".format(self.exited) diff --git a/invoke/tasks.py b/invoke/tasks.py index cd3075e9..a967a283 100644 --- a/invoke/tasks.py +++ b/invoke/tasks.py @@ -21,6 +21,7 @@ Type, TypeVar, Union, + cast, ) from .context import Context @@ -286,7 +287,7 @@ def get_arguments( return args -def task(*args: Any, **kwargs: Any) -> Callable: +def task(*args: Any, **kwargs: Any) -> "Task[T]": """ Marks wrapped callable object as a valid Invoke task. @@ -357,7 +358,7 @@ def inner(body: Callable) -> Task[T]: return _task # update_wrapper(inner, klass) - return inner + return cast('Task["T"]', inner) class Call: diff --git a/invoke/util.py b/invoke/util.py index df29c841..7184ea30 100644 --- a/invoke/util.py +++ b/invoke/util.py @@ -191,7 +191,7 @@ def run(self) -> None: # doesn't appear to be the case, then assume we're being used # directly and just use super() ourselves. # XXX https://github.com/python/mypy/issues/1424 - if hasattr(self, "_run") and callable(self._run): # type: ignore + if hasattr(self, "_run") and callable(self._run): # TODO: this could be: # - io worker with no 'result' (always local) # - tunnel worker, also with no 'result' (also always local) @@ -206,7 +206,7 @@ def run(self) -> None: # and let it continue acting like a normal thread (meh) # - assume the run/sudo/etc case will use a queue inside its # worker body, orthogonal to how exception handling works - self._run() # type: ignore + self._run() else: super().run() except BaseException: From 480155285735dd02bc31bc2fcbfbfaa8699ea503 Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 16:28:00 -0500 Subject: [PATCH 2/8] test: update mypy typings --- dev-requirements.txt | 2 +- invoke/collection.py | 2 +- invoke/util.py | 4 ++-- tasks.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c00b0667..1bf0ad73 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -19,5 +19,5 @@ setuptools>56 # Debuggery icecream>=2.1 # typing -mypy==1.4.1 +mypy==0.971 types-PyYAML==6.0.12.4 diff --git a/invoke/collection.py b/invoke/collection.py index 9c654ca5..20ef3576 100644 --- a/invoke/collection.py +++ b/invoke/collection.py @@ -266,7 +266,7 @@ def add_task( name = task.name # XXX https://github.com/python/mypy/issues/1424 elif hasattr(task.body, "func_name"): - name = task.body.func_name + name = task.body.func_name # type: ignore elif hasattr(task.body, "__name__"): name = task.__name__ else: diff --git a/invoke/util.py b/invoke/util.py index 7184ea30..df29c841 100644 --- a/invoke/util.py +++ b/invoke/util.py @@ -191,7 +191,7 @@ def run(self) -> None: # doesn't appear to be the case, then assume we're being used # directly and just use super() ourselves. # XXX https://github.com/python/mypy/issues/1424 - if hasattr(self, "_run") and callable(self._run): + if hasattr(self, "_run") and callable(self._run): # type: ignore # TODO: this could be: # - io worker with no 'result' (always local) # - tunnel worker, also with no 'result' (also always local) @@ -206,7 +206,7 @@ def run(self) -> None: # and let it continue acting like a normal thread (meh) # - assume the run/sudo/etc case will use a queue inside its # worker body, orthogonal to how exception handling works - self._run() + self._run() # type: ignore else: super().run() except BaseException: diff --git a/tasks.py b/tasks.py index 12e0c3b8..ce845997 100644 --- a/tasks.py +++ b/tasks.py @@ -53,7 +53,7 @@ def test( # TODO: replace with invocations' once the "call truly local tester" problem is # solved (see other TODOs). For now this is just a copy/paste/modify. -@task(help=test.help) # type: ignore +@task(help=test.help) def integration( c: "Context", opts: Optional[str] = None, pty: bool = True ) -> None: From 3df5cf13e2c2f936c4c0fce3592fd987e3cf5479 Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 16:31:00 -0500 Subject: [PATCH 3/8] test: update mypy typings --- invoke/runners.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invoke/runners.py b/invoke/runners.py index a67baa30..913a2187 100644 --- a/invoke/runners.py +++ b/invoke/runners.py @@ -405,7 +405,7 @@ def run( .. versionadded:: 1.0 """ if disowned is not None: - kwargs['disowned'] = disowned + kwargs["disowned"] = disowned try: return self._run_body(command, **kwargs) finally: From 8823b723a498339a3056307ff9b37b2e5e14da72 Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 16:33:52 -0500 Subject: [PATCH 4/8] test: update mypy typings --- invoke/runners.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/invoke/runners.py b/invoke/runners.py index 913a2187..98ada3f9 100644 --- a/invoke/runners.py +++ b/invoke/runners.py @@ -124,9 +124,7 @@ def __init__(self, context: "Context") -> None: self._disowned = False @overload - def run( - self, command: str, *, disowned: None, **kwargs: Any - ) -> "Result": + def run(self, command: str, *, disowned: None, **kwargs: Any) -> "Result": ... @overload From 486183df8432296984c9405216e24a31dbb2d81a Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 16:42:31 -0500 Subject: [PATCH 5/8] test: update mypy typings --- invoke/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invoke/context.py b/invoke/context.py index e9beaf4d..21c5101e 100644 --- a/invoke/context.py +++ b/invoke/context.py @@ -389,7 +389,7 @@ def cd(self, path: Union[PathLike, str]) -> Generator[None, None, None]: (such as the various ``Path`` objects out there), and not just string literals. """ - path = str(path) + path = os.fspath(path) self.command_cwds.append(path) try: yield From 7ff28df2b6c90e9ad47acac50904c9cb5344c382 Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 17:53:39 -0500 Subject: [PATCH 6/8] test: update mypy typings --- tests/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/context.py b/tests/context.py index b7266042..d1b03188 100644 --- a/tests/context.py +++ b/tests/context.py @@ -213,7 +213,7 @@ class Path: def __init__(self, value): self.value = value - def __str__(self): + def __fspath__(self): return self.value runner = Local.return_value From 87b0ba59c1009d79ba8af3e9306b7f249cce6aa5 Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Mon, 10 Jul 2023 17:59:58 -0500 Subject: [PATCH 7/8] test: update mypy typings --- invoke/runners.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/invoke/runners.py b/invoke/runners.py index 98ada3f9..3a24195e 100644 --- a/invoke/runners.py +++ b/invoke/runners.py @@ -131,9 +131,7 @@ def run(self, command: str, *, disowned: None, **kwargs: Any) -> "Result": def run(self, command: str, *, disowned: bool, **kwargs: Any) -> None: ... - def run( - self, command: str, *, disowned: Optional[bool] = None, **kwargs: Any - ) -> Optional["Result"]: + def run(self, command: str, **kwargs: Any) -> Optional["Result"]: """ Execute ``command``, returning an instance of `Result` once complete. @@ -402,8 +400,6 @@ def run( .. versionadded:: 1.0 """ - if disowned is not None: - kwargs["disowned"] = disowned try: return self._run_body(command, **kwargs) finally: From bff4687a148a01288dc804b7490c82416da0b5af Mon Sep 17 00:00:00 2001 From: "Jesse P. Johnson" Date: Tue, 11 Jul 2023 10:02:08 -0500 Subject: [PATCH 8/8] test: update return type for mypy to optional return result when disowned --- invoke/runners.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/invoke/runners.py b/invoke/runners.py index 3a24195e..5ac3cd52 100644 --- a/invoke/runners.py +++ b/invoke/runners.py @@ -128,7 +128,9 @@ def run(self, command: str, *, disowned: None, **kwargs: Any) -> "Result": ... @overload - def run(self, command: str, *, disowned: bool, **kwargs: Any) -> None: + def run( + self, command: str, *, disowned: bool, **kwargs: Any + ) -> Optional["Result"]: ... def run(self, command: str, **kwargs: Any) -> Optional["Result"]: