Skip to content

Commit

Permalink
Fix bug where support for Path objects was missing from MockPopen
Browse files Browse the repository at this point in the history
  • Loading branch information
cjw296 committed Oct 20, 2023
1 parent 3347ced commit b814a3a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 5 deletions.
21 changes: 16 additions & 5 deletions testfixtures/popen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@
from functools import wraps, partial, reduce
from io import TextIOWrapper
from itertools import chain, zip_longest
from os import PathLike
from subprocess import STDOUT, PIPE
from tempfile import TemporaryFile
from testfixtures.utils import extend_docstring
from typing import Union, Callable, List, Optional, Sequence, Tuple, Dict
from typing import Union, Callable, List, Optional, Sequence, Tuple, Dict, Iterable
from .mock import Mock, call, _Call as Call


AnyStr = Union[str, bytes]
Command = Union[str, Sequence[str]]
Command = Union[str, PathLike, Sequence[str]]


def shell_join(command: Command) -> str:
if not isinstance(command, str):
command = " ".join(shlex.quote(part) for part in command)
return command
if isinstance(command, str):
return command
elif isinstance(command, PathLike):
return str(command)
elif isinstance(command, Iterable):
quoted_parts = []
for part in command:
if not isinstance(part, str):
raise TypeError(f'{part!r} in {command} was {type(part)}, must be str')
quoted_parts.append(shlex.quote(part))
return " ".join(quoted_parts)
else:
raise TypeError(f'{command!r} was {type(command)}, must be str')


class PopenBehaviour(object):
Expand Down
25 changes: 25 additions & 0 deletions testfixtures/tests/test_popen.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import subprocess
from pathlib import Path
from subprocess import PIPE, STDOUT
from unittest import TestCase

Expand Down Expand Up @@ -84,6 +85,30 @@ def test_command_is_sequence(self):
call.Popen_instance.wait(),
], Popen.mock.method_calls)

def test_command_is_pathlike(self):
Popen = MockPopen()
Popen.set_command('a command')

process = Popen(Path('a command'))

compare(process.wait(), 0)
compare([
call.Popen(Path('a command')),
call.Popen_instance.wait(),
], Popen.mock.method_calls)

def test_command_is_incorrect_type(self):
Popen = MockPopen()
Popen.set_command('a command')
with ShouldRaise(TypeError("42 was <class 'int'>, must be str")):
Popen(42)

def test_command_is_sequence_of_incorrect_type(self):
Popen = MockPopen()
Popen.set_command('a command')
with ShouldRaise(TypeError("42 in ['x', 42] was <class 'int'>, must be str")):
Popen(['x', 42])

def test_communicate_with_input(self):
# setup
Popen = MockPopen()
Expand Down

0 comments on commit b814a3a

Please sign in to comment.