Skip to content

Commit

Permalink
wrap ProcessDispatcher.dispatch into FakePopenWrapper (#170)
Browse files Browse the repository at this point in the history
* add FakePopenWrapper

* changelog entry

---------

Co-authored-by: Michal Poddubiuk <mpoddubi@akamai.com>
  • Loading branch information
meeshal and Michal Poddubiuk authored Oct 3, 2024
1 parent 570fbaf commit 9aacf03
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
6 changes: 6 additions & 0 deletions changelog.d/bug.c797773a.entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
message: Wrapped ProcessDispatcher.dispatch into FakePopenWrapper as it was causing
TypeError when Popen is used as a type.
pr_ids:
- '170'
timestamp: 1727883418
type: bug
14 changes: 13 additions & 1 deletion pytest_subprocess/process_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
from copy import deepcopy
from functools import partial
from typing import Any as AnyType
from typing import AnyStr
from typing import Awaitable
from typing import Callable
from typing import Deque
from typing import Dict
from typing import Generic
from typing import List
from typing import Optional
from typing import Tuple
Expand All @@ -29,6 +31,9 @@
from .fake_process import FakeProcess


__all__ = ["ProcessDispatcher"]


class ProcessDispatcher:
"""Main class for handling processes."""

Expand All @@ -44,7 +49,7 @@ class ProcessDispatcher:
def register(cls, process: "FakeProcess") -> None:
if not cls.process_list:
cls.built_in_popen = subprocess.Popen
subprocess.Popen = cls.dispatch # type: ignore
subprocess.Popen = FakePopenWrapper # type: ignore

cls.built_in_async_subprocess = asyncio.subprocess
asyncio.create_subprocess_shell = cls.async_shell # type: ignore
Expand Down Expand Up @@ -238,3 +243,10 @@ def _get_process(
if processes and isinstance(processes, deque):
return command_instance, processes, process_instance
return None, None, None


class FakePopenWrapper(Generic[AnyStr]):
def __new__( # type: ignore
cls, command: COMMAND, **kwargs: Optional[Dict]
) -> FakePopen:
return ProcessDispatcher.dispatch(command, **kwargs) # type: ignore
22 changes: 22 additions & 0 deletions tests/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,3 +1232,25 @@ def test_process_recorder(fp):
recorder.clear()

assert not recorder.was_called()


def test_fake_popen_is_typed(fp):
fp.allow_unregistered(True)
fp.register(
[PYTHON, "example_script.py"],
stdout=b"Stdout line 1\r\nStdout line 2\r\n",
)

def spawn_process() -> subprocess.Popen[str]:
import subprocess

return subprocess.Popen(
(PYTHON, "example_script.py"),
universal_newlines=True,
stdout=subprocess.PIPE,
)

proc = spawn_process()
proc.wait()

assert proc.stdout.read() == "Stdout line 1\nStdout line 2\n"

0 comments on commit 9aacf03

Please sign in to comment.