From 61df8230dfdb0966b3ccd86adf104ac036f6c7ea Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:13:50 -0600 Subject: [PATCH 01/12] internal/subprocess(refactor[typing]): Tighten subprocess param types why: Reduce use of overly broad types for SubprocessCommand parameters. what: - Narrow _FILE to IO[str] | IO[bytes] - Specify preexec_fn returns None - Constrain pass_fds to sequence of ints --- src/libvcs/_internal/subprocess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libvcs/_internal/subprocess.py b/src/libvcs/_internal/subprocess.py index 003de28f2..55548b71a 100644 --- a/src/libvcs/_internal/subprocess.py +++ b/src/libvcs/_internal/subprocess.py @@ -63,7 +63,7 @@ def __init__(self, output: str, *args: object) -> None: _ENV: t.TypeAlias = Mapping[str, str] else: _ENV: t.TypeAlias = Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] -_FILE: t.TypeAlias = None | int | t.IO[t.Any] +_FILE: t.TypeAlias = None | int | t.IO[str] | t.IO[bytes] _TXT: t.TypeAlias = bytes | str #: Command _CMD: t.TypeAlias = StrOrBytesPath | Sequence[StrOrBytesPath] @@ -96,7 +96,7 @@ class SubprocessCommand(SkipDefaultFieldsReprMixin): stdin: _FILE = None stdout: _FILE = None stderr: _FILE = None - preexec_fn: t.Callable[[], t.Any] | None = None + preexec_fn: t.Callable[[], None] | None = None close_fds: bool = True shell: bool = False cwd: StrOrBytesPath | None = None @@ -109,7 +109,7 @@ class SubprocessCommand(SkipDefaultFieldsReprMixin): # POSIX-only restore_signals: bool = True start_new_session: bool = False - pass_fds: t.Any = () + pass_fds: Sequence[int] = () umask: int = -1 pipesize: int = -1 user: str | None = None From 61d0401b91cbd9a62223e6a277645bd850006d72 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:15:40 -0600 Subject: [PATCH 02/12] internal/run(refactor[typing]): Tighten run() parameter types why: Reduce overly broad typing on subprocess helper parameters. what: - Narrow _FILE to IO[str] | IO[bytes] - Specify preexec_fn returns None - Constrain pass_fds to sequence of ints --- src/libvcs/_internal/run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libvcs/_internal/run.py b/src/libvcs/_internal/run.py index 8b586ba8a..ef82ce65f 100644 --- a/src/libvcs/_internal/run.py +++ b/src/libvcs/_internal/run.py @@ -100,7 +100,7 @@ def __call__(self, output: str, timestamp: datetime.datetime) -> None: _ENV: t.TypeAlias = Mapping[bytes, StrPath] | Mapping[str, StrPath] _CMD = StrPath | Sequence[StrPath] -_FILE: t.TypeAlias = int | t.IO[t.Any] | None +_FILE: t.TypeAlias = int | t.IO[str] | t.IO[bytes] | None def run( @@ -110,7 +110,7 @@ def run( stdin: _FILE | None = None, stdout: _FILE | None = None, stderr: _FILE | None = None, - preexec_fn: t.Callable[[], t.Any] | None = None, + preexec_fn: t.Callable[[], None] | None = None, close_fds: bool = True, shell: bool = False, cwd: StrPath | None = None, @@ -119,7 +119,7 @@ def run( creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: t.Any = (), + pass_fds: Sequence[int] = (), *, encoding: str | None = None, errors: str | None = None, From 896ffd8c7ee84c6ba8e3bbaac934591363d3118e Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:21:31 -0600 Subject: [PATCH 03/12] tests/_internal/subprocess(test[typing]): Refine SubprocessCommand test types why: Reduce broad typing in test fixtures without changing behavior. what: - Replace varargs fixture shape with explicit cmd_args - Type expected_result as SubprocessCommand - Add CmdArgs alias for clearer argument intent --- .../subprocess/test_SubprocessCommand.py | 107 ++++++++++-------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/tests/_internal/subprocess/test_SubprocessCommand.py b/tests/_internal/subprocess/test_SubprocessCommand.py index 345d2912e..7061febf8 100644 --- a/tests/_internal/subprocess/test_SubprocessCommand.py +++ b/tests/_internal/subprocess/test_SubprocessCommand.py @@ -8,12 +8,17 @@ import pytest from libvcs._internal.subprocess import SubprocessCommand +from libvcs._internal.types import StrOrBytesPath if t.TYPE_CHECKING: import pathlib -def idfn(val: t.Any) -> str: +CmdArgs = StrOrBytesPath | t.Sequence[StrOrBytesPath] +Kwargs = dict[str, t.Any] + + +def idfn(val: CmdArgs | Kwargs | SubprocessCommand | None) -> str: """Test ID naming function for SubprocessCommand py.test parametrize.""" if isinstance(val, list): if len(val): @@ -24,15 +29,19 @@ def idfn(val: t.Any) -> str: @pytest.mark.parametrize( - ("args", "kwargs", "expected_result"), + ("cmd_args", "kwargs", "expected_result"), [ - (["ls"], {}, SubprocessCommand("ls")), - ([["ls", "-l"]], {}, SubprocessCommand(["ls", "-l"])), - ([], {"args": ["ls", "-l"]}, SubprocessCommand(["ls", "-l"])), - (["ls -l"], {"shell": True}, SubprocessCommand("ls -l", shell=True)), - ([], {"args": "ls -l", "shell": True}, SubprocessCommand("ls -l", shell=True)), + ("ls", {}, SubprocessCommand("ls")), + (["ls", "-l"], {}, SubprocessCommand(["ls", "-l"])), + (None, {"args": ["ls", "-l"]}, SubprocessCommand(["ls", "-l"])), + ("ls -l", {"shell": True}, SubprocessCommand("ls -l", shell=True)), + ( + None, + {"args": "ls -l", "shell": True}, + SubprocessCommand("ls -l", shell=True), + ), ( - [], + None, {"args": ["ls", "-l"], "shell": True}, SubprocessCommand(["ls", "-l"], shell=True), ), @@ -40,12 +49,16 @@ def idfn(val: t.Any) -> str: ids=idfn, ) def test_init( - args: list[t.Any], - kwargs: dict[str, t.Any], - expected_result: t.Any, + cmd_args: CmdArgs | None, + kwargs: Kwargs, + expected_result: SubprocessCommand, ) -> None: """Test SubprocessCommand via list + kwargs, assert attributes.""" - cmd = SubprocessCommand(*args, **kwargs) + cmd = ( + SubprocessCommand(cmd_args, **kwargs) + if cmd_args is not None + else SubprocessCommand(**kwargs) + ) assert cmd == expected_result # Attributes in cmd should match what's passed in @@ -57,116 +70,116 @@ def test_init( assert proc.returncode == 0 -FIXTURES = [ - [["ls"], {}, SubprocessCommand("ls")], - [[["ls", "-l"]], {}, SubprocessCommand(["ls", "-l"])], +FIXTURES: list[tuple[CmdArgs, Kwargs, SubprocessCommand]] = [ + ("ls", {}, SubprocessCommand("ls")), + (["ls", "-l"], {}, SubprocessCommand(["ls", "-l"])), ] @pytest.mark.parametrize( - ("args", "kwargs", "expected_result"), + ("cmd_args", "kwargs", "expected_result"), FIXTURES, ids=idfn, ) def test_init_and_Popen( - args: list[t.Any], - kwargs: dict[str, t.Any], - expected_result: t.Any, + cmd_args: CmdArgs, + kwargs: Kwargs, + expected_result: SubprocessCommand, ) -> None: """Test SubprocessCommand with Popen.""" - cmd = SubprocessCommand(*args, **kwargs) + cmd = SubprocessCommand(cmd_args, **kwargs) assert cmd == expected_result cmd_proc = cmd.Popen() cmd_proc.communicate() assert cmd_proc.returncode == 0 - proc = subprocess.Popen(*args, **kwargs) + proc = subprocess.Popen(cmd_args, **kwargs) proc.communicate() assert proc.returncode == 0 @pytest.mark.parametrize( - ("args", "kwargs", "expected_result"), + ("cmd_args", "kwargs", "expected_result"), FIXTURES, ids=idfn, ) def test_init_and_Popen_run( - args: list[t.Any], - kwargs: dict[str, t.Any], - expected_result: t.Any, + cmd_args: CmdArgs, + kwargs: Kwargs, + expected_result: SubprocessCommand, ) -> None: """Test SubprocessCommand with run.""" - cmd = SubprocessCommand(*args, **kwargs) + cmd = SubprocessCommand(cmd_args, **kwargs) assert cmd == expected_result cmd_proc = cmd.Popen() cmd_proc.communicate() assert cmd_proc.returncode == 0 - proc = subprocess.run(*args, **kwargs, check=False) + proc = subprocess.run(cmd_args, **kwargs, check=False) assert proc.returncode == 0 @pytest.mark.parametrize( - ("args", "kwargs", "expected_result"), + ("cmd_args", "kwargs", "expected_result"), FIXTURES, ids=idfn, ) def test_init_and_check_call( - args: list[t.Any], - kwargs: dict[str, t.Any], - expected_result: t.Any, + cmd_args: CmdArgs, + kwargs: Kwargs, + expected_result: SubprocessCommand, ) -> None: """Test SubprocessCommand with Popen.check_call.""" - cmd = SubprocessCommand(*args, **kwargs) + cmd = SubprocessCommand(cmd_args, **kwargs) assert cmd == expected_result return_code = cmd.check_call() assert return_code == 0 - proc = subprocess.check_call(*args, **kwargs) + proc = subprocess.check_call(cmd_args, **kwargs) assert proc == return_code @pytest.mark.parametrize( - ("args", "kwargs", "expected_result"), + ("cmd_args", "kwargs", "expected_result"), FIXTURES, ) def test_init_and_check_output( - args: list[t.Any], - kwargs: dict[str, t.Any], - expected_result: t.Any, + cmd_args: CmdArgs, + kwargs: Kwargs, + expected_result: SubprocessCommand, ) -> None: """Test SubprocessCommand with Popen.check_output.""" - cmd = SubprocessCommand(*args, **kwargs) + cmd = SubprocessCommand(cmd_args, **kwargs) assert cmd == expected_result return_output = cmd.check_output() assert isinstance(return_output, bytes) - proc = subprocess.check_output(*args, **kwargs) + proc = subprocess.check_output(cmd_args, **kwargs) assert proc == return_output @pytest.mark.parametrize( - ("args", "kwargs", "run_kwargs"), + ("cmd_args", "kwargs", "run_kwargs"), [ - (["ls"], {}, {}), - ([["ls", "-l"]], {}, {}), - ([["ls", "-al"]], {}, {"stdout": subprocess.DEVNULL}), + ("ls", {}, {}), + (["ls", "-l"], {}, {}), + (["ls", "-al"], {}, {"stdout": subprocess.DEVNULL}), ], ids=idfn, ) def test_run( tmp_path: pathlib.Path, - args: list[t.Any], - kwargs: dict[str, t.Any], - run_kwargs: dict[str, t.Any], + cmd_args: CmdArgs, + kwargs: Kwargs, + run_kwargs: Kwargs, ) -> None: """Test SubprocessCommand.run().""" kwargs["cwd"] = tmp_path - cmd = SubprocessCommand(*args, **kwargs) + cmd = SubprocessCommand(cmd_args, **kwargs) response = cmd.run(**run_kwargs) assert response.returncode == 0 From 1159095e6b5311d2e71075f87af0783f6f41d069 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:22:28 -0600 Subject: [PATCH 04/12] tests/cmd/test_git(test[typing]): Narrow constructor param type why: Keep the test fixture typing aligned with actual callable returns. what: - Replace t.Any return with str | pathlib.Path for path_type --- tests/cmd/test_git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cmd/test_git.py b/tests/cmd/test_git.py index 0dfe46baa..072279e07 100644 --- a/tests/cmd/test_git.py +++ b/tests/cmd/test_git.py @@ -18,7 +18,7 @@ @pytest.mark.parametrize("path_type", [str, pathlib.Path]) def test_git_constructor( - path_type: t.Callable[[str | pathlib.Path], t.Any], + path_type: t.Callable[[str | pathlib.Path], str | pathlib.Path], tmp_path: pathlib.Path, ) -> None: """Test Git constructor.""" From df34e0fb0bbe436c663cb0e5cb230529f3ea1f60 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:24:36 -0600 Subject: [PATCH 05/12] internal/shortcuts(refactor[typing]): Narrow VCS TypeGuard input why: Avoid unbounded Any in local VCS guard. what: - Type is_vcs input as str --- src/libvcs/_internal/shortcuts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libvcs/_internal/shortcuts.py b/src/libvcs/_internal/shortcuts.py index a3e401d33..53507e9a2 100644 --- a/src/libvcs/_internal/shortcuts.py +++ b/src/libvcs/_internal/shortcuts.py @@ -127,7 +127,7 @@ def create_project( assert vcs_matches[0].vcs is not None - def is_vcs(val: t.Any) -> t.TypeGuard[VCSLiteral]: + def is_vcs(val: str) -> t.TypeGuard[VCSLiteral]: return isinstance(val, str) and val in {"git", "hg", "svn"} if is_vcs(vcs_matches[0].vcs): From 173c2c86f695a4a76cbb667131a507aa55dd7c01 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:29:33 -0600 Subject: [PATCH 06/12] cmd/git(refactor[typing]): Type reftag as str why: reftag is passed to git CLI as a ref name or URL. what: - Narrow fetch() and pull() reftag to str | None --- src/libvcs/cmd/git.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libvcs/cmd/git.py b/src/libvcs/cmd/git.py index 23fd21d25..d46825a3c 100644 --- a/src/libvcs/cmd/git.py +++ b/src/libvcs/cmd/git.py @@ -420,7 +420,7 @@ def clone( def fetch( self, *, - reftag: t.Any | None = None, + reftag: str | None = None, deepen: str | None = None, depth: str | None = None, upload_pack: str | None = None, @@ -788,7 +788,7 @@ def rebase( def pull( self, *, - reftag: t.Any | None = None, + reftag: str | None = None, repository: str | None = None, deepen: str | None = None, depth: str | None = None, From 038dc69a3368a5f6833a220467ac74816c32d87c Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:31:16 -0600 Subject: [PATCH 07/12] cmd/git(refactor[typing]): Narrow config value types why: Document the supported shapes for git --config values. what: - Add GitConfigValue alias and use it for config parameters - Type the config stringify helper to match --- src/libvcs/cmd/git.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libvcs/cmd/git.py b/src/libvcs/cmd/git.py index d46825a3c..f2ab6425a 100644 --- a/src/libvcs/cmd/git.py +++ b/src/libvcs/cmd/git.py @@ -16,6 +16,7 @@ from libvcs._internal.types import StrOrBytesPath, StrPath _CMD = StrOrBytesPath | Sequence[StrOrBytesPath] +GitConfigValue: t.TypeAlias = bool | int | float | StrPath class Git: @@ -141,7 +142,7 @@ def run( noglob_pathspecs: bool | None = None, icase_pathspecs: bool | None = None, no_optional_locks: bool | None = None, - config: dict[str, t.Any] | None = None, + config: dict[str, GitConfigValue] | None = None, config_env: str | None = None, # Pass-through to run() log_in_real_time: bool = False, @@ -241,7 +242,7 @@ def run( if config is not None: assert isinstance(config, dict) - def stringify(v: t.Any) -> str: + def stringify(v: GitConfigValue) -> str: if isinstance(v, bool): return "true" if v else "false" if not isinstance(v, str): @@ -316,7 +317,7 @@ def clone( verbose: bool | None = None, quiet: bool | None = None, # Pass-through to run - config: dict[str, t.Any] | None = None, + config: dict[str, GitConfigValue] | None = None, log_in_real_time: bool = False, # Special behavior check_returncode: bool | None = None, From 7f4049a82897aacda0756ff641a2c076e045c844 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:32:13 -0600 Subject: [PATCH 08/12] internal/run(refactor[typing]): Narrow log adapter return type why: process() always returns the input message string. what: - Type CmdLoggingAdapter.process return as tuple[str, ...] --- src/libvcs/_internal/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libvcs/_internal/run.py b/src/libvcs/_internal/run.py index ef82ce65f..466e7afc5 100644 --- a/src/libvcs/_internal/run.py +++ b/src/libvcs/_internal/run.py @@ -75,7 +75,7 @@ def process( self, msg: str, kwargs: MutableMapping[str, t.Any], - ) -> tuple[t.Any, MutableMapping[str, t.Any]]: + ) -> tuple[str, MutableMapping[str, t.Any]]: """Add additional context information for loggers.""" prefixed_dict = {} prefixed_dict["bin_name"] = self.bin_name From 48bd165008205a3df7c2f9afd9f536b0d82446cd Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:43:32 -0600 Subject: [PATCH 09/12] cmd/git(refactor[typing]): Type submodule status data why: Git submodule parsing returns a fixed schema. what: - Add GitSubmoduleData TypedDict - Use it for _ls() return and list typing --- src/libvcs/cmd/git.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/libvcs/cmd/git.py b/src/libvcs/cmd/git.py index f2ab6425a..42cb41d1e 100644 --- a/src/libvcs/cmd/git.py +++ b/src/libvcs/cmd/git.py @@ -19,6 +19,18 @@ GitConfigValue: t.TypeAlias = bool | int | float | StrPath +class GitSubmoduleData(t.TypedDict): + """Structured submodule data returned by _ls().""" + + name: str + path: str + sha: str + url: str | None + branch: str | None + status_prefix: str + description: str + + class Git: """Run commands directly on a git repository.""" @@ -3387,7 +3399,7 @@ def _ls( # Pass-through to run() log_in_real_time: bool = False, check_returncode: bool | None = None, - ) -> list[dict[str, t.Any]]: + ) -> list[GitSubmoduleData]: """Parse submodule status output into structured data. Parameters @@ -3399,7 +3411,7 @@ def _ls( Returns ------- - list[dict[str, Any]] + list[GitSubmoduleData] List of parsed submodule data. Examples @@ -3422,7 +3434,7 @@ def _ls( log_in_real_time=log_in_real_time, ) - submodules: list[dict[str, t.Any]] = [] + submodules: list[GitSubmoduleData] = [] for line in result.strip().split("\n"): if not line: From 7e22cf986331f0e37f0b47ace32b734bd12a0bd6 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 06:46:23 -0600 Subject: [PATCH 10/12] internal/shortcuts(refactor[typing]): Tighten kwargs key type why: **kwargs keys are always strings for create_project. what: - Use dict[str, t.Any] for create_project overloads and impl --- src/libvcs/_internal/shortcuts.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libvcs/_internal/shortcuts.py b/src/libvcs/_internal/shortcuts.py index 53507e9a2..57d81e56e 100644 --- a/src/libvcs/_internal/shortcuts.py +++ b/src/libvcs/_internal/shortcuts.py @@ -38,7 +38,7 @@ def create_project( path: StrPath, vcs: t.Literal["git"], progress_callback: ProgressCallbackProtocol | None = None, - **kwargs: dict[t.Any, t.Any], + **kwargs: dict[str, t.Any], ) -> GitSync: ... @@ -49,7 +49,7 @@ def create_project( path: StrPath, vcs: t.Literal["svn"], progress_callback: ProgressCallbackProtocol | None = None, - **kwargs: dict[t.Any, t.Any], + **kwargs: dict[str, t.Any], ) -> SvnSync: ... @@ -60,7 +60,7 @@ def create_project( path: StrPath, vcs: t.Literal["hg"], progress_callback: ProgressCallbackProtocol | None = ..., - **kwargs: dict[t.Any, t.Any], + **kwargs: dict[str, t.Any], ) -> HgSync: ... @@ -71,7 +71,7 @@ def create_project( path: StrPath, vcs: None = None, progress_callback: ProgressCallbackProtocol | None = None, - **kwargs: dict[t.Any, t.Any], + **kwargs: dict[str, t.Any], ) -> GitSync | HgSync | SvnSync: ... @@ -81,7 +81,7 @@ def create_project( path: StrPath, vcs: VCSLiteral | None = None, progress_callback: ProgressCallbackProtocol | None = None, - **kwargs: dict[t.Any, t.Any], + **kwargs: dict[str, t.Any], ) -> GitSync | HgSync | SvnSync: r"""Return an object representation of a VCS repository. From 5ffc22b2bcef9e5cf799ac2a5c7f909a7ab96852 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 07:30:43 -0600 Subject: [PATCH 11/12] internal/subprocess(refactor[typing]): Align pass_fds with typeshed why: Use Collection[int] to match Python's official typeshed, allowing sets and frozensets which subprocess accepts at runtime. what: - Change pass_fds type from Sequence[int] to Collection[int] - Update both subprocess.py and run.py --- src/libvcs/_internal/run.py | 4 ++-- src/libvcs/_internal/subprocess.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libvcs/_internal/run.py b/src/libvcs/_internal/run.py index 466e7afc5..e4bdfacdf 100644 --- a/src/libvcs/_internal/run.py +++ b/src/libvcs/_internal/run.py @@ -15,7 +15,7 @@ import subprocess import sys import typing as t -from collections.abc import Iterable, Mapping, MutableMapping, Sequence +from collections.abc import Collection, Iterable, Mapping, MutableMapping, Sequence from libvcs import exc from libvcs._internal.types import StrPath @@ -119,7 +119,7 @@ def run( creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Sequence[int] = (), + pass_fds: Collection[int] = (), *, encoding: str | None = None, errors: str | None = None, diff --git a/src/libvcs/_internal/subprocess.py b/src/libvcs/_internal/subprocess.py index 55548b71a..ff87a2dfb 100644 --- a/src/libvcs/_internal/subprocess.py +++ b/src/libvcs/_internal/subprocess.py @@ -45,7 +45,7 @@ import subprocess import sys import typing as t -from collections.abc import Mapping, Sequence +from collections.abc import Collection, Mapping, Sequence from libvcs._internal.types import StrOrBytesPath @@ -109,7 +109,7 @@ class SubprocessCommand(SkipDefaultFieldsReprMixin): # POSIX-only restore_signals: bool = True start_new_session: bool = False - pass_fds: Sequence[int] = () + pass_fds: Collection[int] = () umask: int = -1 pipesize: int = -1 user: str | None = None From deb99e5ca251f92fcc2f5212a0ea653c6185b75b Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 3 Jan 2026 07:31:41 -0600 Subject: [PATCH 12/12] internal/subprocess(refactor[typing]): Align preexec_fn with typeshed why: Use Callable[[], object] to match Python's official typeshed, accepting functions that return values (which subprocess ignores). what: - Change preexec_fn return type from None to object - Update both subprocess.py and run.py --- src/libvcs/_internal/run.py | 2 +- src/libvcs/_internal/subprocess.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libvcs/_internal/run.py b/src/libvcs/_internal/run.py index e4bdfacdf..d68413ab8 100644 --- a/src/libvcs/_internal/run.py +++ b/src/libvcs/_internal/run.py @@ -110,7 +110,7 @@ def run( stdin: _FILE | None = None, stdout: _FILE | None = None, stderr: _FILE | None = None, - preexec_fn: t.Callable[[], None] | None = None, + preexec_fn: t.Callable[[], object] | None = None, close_fds: bool = True, shell: bool = False, cwd: StrPath | None = None, diff --git a/src/libvcs/_internal/subprocess.py b/src/libvcs/_internal/subprocess.py index ff87a2dfb..7e677dce8 100644 --- a/src/libvcs/_internal/subprocess.py +++ b/src/libvcs/_internal/subprocess.py @@ -96,7 +96,7 @@ class SubprocessCommand(SkipDefaultFieldsReprMixin): stdin: _FILE = None stdout: _FILE = None stderr: _FILE = None - preexec_fn: t.Callable[[], None] | None = None + preexec_fn: t.Callable[[], object] | None = None close_fds: bool = True shell: bool = False cwd: StrOrBytesPath | None = None