Codebase list python-procrunner / ef94664
Fix merge conflicts Markus Gerstel 2 years ago
13 changed file(s) with 43 addition(s) and 76 deletion(s). Raw diff Collapse all Expand all
1111 continue
1212 filename = os.path.normpath(os.path.join(base, f))
1313 try:
14 with open(filename, "r") as fh:
14 with open(filename) as fh:
1515 ast.parse(fh.read())
1616 except SyntaxError as se:
1717 failures += 1
2525 vmImage: ubuntu-latest
2626 strategy:
2727 matrix:
28 python35:
29 PYTHON_VERSION: 3.5
3028 python36:
3129 PYTHON_VERSION: 3.6
3230 python37:
4240 vmImage: macOS-latest
4341 strategy:
4442 matrix:
45 python35:
46 PYTHON_VERSION: 3.5
4743 python36:
4844 PYTHON_VERSION: 3.6
4945 python37:
5955 vmImage: windows-latest
6056 strategy:
6157 matrix:
62 python35:
63 PYTHON_VERSION: 3.5
6458 python36:
6559 PYTHON_VERSION: 3.6
6660 python37:
88 directory: "/" # Location of package manifests
99 schedule:
1010 interval: "monthly"
11 ignore:
12 - dependency-name: "mock"
13 # mock 4 requires Python 3.6+
14 versions: [">=4"]
15 - dependency-name: "twine"
16 # twine 2 requires Python 3.6+
17 versions: [">=2"]
1111 - python: 3.8
1212 - python: 3.7
1313 - python: 3.6
14 - python: 3.5
1514 - os: osx
1615 language: generic
1716 env: CONDA=3.8 TOXENV=py38
2120 - os: osx
2221 language: generic
2322 env: CONDA=3.6 TOXENV=py36
24 - os: osx
25 language: generic
26 env: CONDA=3.5 TOXENV=py35
2723
2824 allow_failures:
2925 - env: OPTIONAL=1
00 =======
11 History
22 =======
3
4 2.3.0 (????-??-??)
5 ------------------
6 * Python 3.6+ only, support for Python 3.5 has been dropped
37
48 2.2.0 (2020-09-07)
59 ------------------
2731
2832 2.0.0 (2020-06-24)
2933 ------------------
30
3134 * Python 3.5+ only, support for Python 2.7 has been dropped
3235 * Deprecated function alias run_process() has been removed
3336 * Fixed a stability issue on Windows
3437
3538 1.1.0 (2019-11-04)
3639 ------------------
37
3840 * Add Python 3.8 support, drop Python 3.4 support
3941
4042 1.0.2 (2019-05-20)
4143 ------------------
42
4344 * Stop environment override variables leaking into the process environment
4445
4546 1.0.1 (2019-04-16)
4647 ------------------
47
4848 * Minor fixes on the return object (implement equality,
4949 mark as unhashable)
5050
5151 1.0.0 (2019-03-25)
5252 ------------------
53
5453 * Support file system path objects (PEP-519) in arguments
5554 * Change the return object to make it similar to
5655 subprocess.CompletedProcess, introduced with Python 3.5+
5756
5857 0.9.1 (2019-02-22)
5958 ------------------
60
6159 * Have deprecation warnings point to correct code locations
6260
6361 0.9.0 (2018-12-07)
6462 ------------------
65
6663 * Trap UnicodeEncodeError when printing output. Offending characters
6764 are replaced and a warning is logged once. Hints at incorrectly set
6865 PYTHONIOENCODING.
6966
7067 0.8.1 (2018-12-04)
7168 ------------------
72
7369 * Fix a few deprecation warnings
7470
7571 0.8.0 (2018-10-09)
7672 ------------------
77
7873 * Add parameter working_directory to set the working directory
7974 of the subprocess
8075
8176 0.7.2 (2018-10-05)
8277 ------------------
83
8478 * Officially support Python 3.7
8579
8680 0.7.1 (2018-09-03)
8781 ------------------
88
8982 * Accept environment variable overriding with numeric values.
9083
9184 0.7.0 (2018-05-13)
9285 ------------------
93
9486 * Unicode fixes. Fix crash on invalid UTF-8 input.
9587 * Clarify that stdout/stderr values are returned as bytestrings.
9688 * Callbacks receive the data decoded as UTF-8 unicode strings
10092
10193 0.6.1 (2018-05-02)
10294 ------------------
103
10495 * Maintenance release to add some tests for executable resolution.
10596
10697 0.6.0 (2018-05-02)
10798 ------------------
108
10999 * Fix Win32 API executable resolution for commands containing a dot ('.') in
110100 addition to a file extension (say '.bat').
111101
112102 0.5.1 (2018-04-27)
113103 ------------------
114
115104 * Fix Win32API dependency installation on Windows.
116105
117106 0.5.0 (2018-04-26)
118107 ------------------
119
120108 * New keyword 'win32resolve' which only takes effect on Windows and is enabled
121109 by default. This causes procrunner to call the Win32 API FindExecutable()
122110 function to try and lookup non-.exe files with the corresponding name. This
125113
126114 0.4.0 (2018-04-23)
127115 ------------------
128
129116 * Python 2.7 support on Windows. Python3 not yet supported on Windows.
130117
131118 0.3.0 (2018-04-17)
132119 ------------------
133
134120 * run_process() renamed to run()
135121 * Python3 compatibility fixes
136122
137123 0.2.0 (2018-03-12)
138124 ------------------
139
140125 * Procrunner is now Python3 3.3-3.6 compatible.
141126
142127 0.1.0 (2018-03-12)
143128 ------------------
144
145129 * First release on PyPI.
44 # For Python versions available on Appveyor, see
55 # http://www.appveyor.com/docs/installed-software#python
66
7 - PYTHON: "C:\\Python35"
87 - PYTHON: "C:\\Python36"
98 - PYTHON: "C:\\Python37"
109 - PYTHON: "C:\\Python38"
11 - PYTHON: "C:\\Python35-x64"
1210 - PYTHON: "C:\\Python36-x64"
1311 - PYTHON: "C:\\Python37-x64"
1412 - PYTHON: "C:\\Python38-x64"
274274 return obj
275275
276276
277 def _windows_resolve(command):
277 def _windows_resolve(command, path=None):
278278 """
279279 Try and find the full path and file extension of the executable to run.
280280 This is so that e.g. calls to 'somescript' will point at 'somescript.cmd'
289289 if not command or not isinstance(command[0], str):
290290 return command
291291
292 found_executable = shutil.which(command[0])
292 found_executable = shutil.which(command[0], path=path)
293293 if found_executable:
294294 logger.debug("Resolved %s as %s", command[0], found_executable)
295295 return (found_executable, *command[1:])
298298 # Special case. shutil.which may not detect file extensions if a full
299299 # path is given, so try to resolve the executable explicitly
300300 for extension in os.getenv("PATHEXT").split(os.pathsep):
301 found_executable = shutil.which(command[0] + extension)
301 found_executable = shutil.which(command[0] + extension, path=path)
302302 if found_executable:
303303 return (found_executable, *command[1:])
304304
334334 if key in self._extras:
335335 return self._extras[key]
336336 if not hasattr(self, key):
337 raise KeyError("Unknown attribute {key}".format(key=key))
337 raise KeyError(f"Unknown attribute {key}")
338338 return getattr(self, key)
339339
340340 def __eq__(self, other):
517517 command = tuple(_path_resolve(part) for part in command)
518518 if win32resolve and sys.platform == "win32":
519519 command = _windows_resolve(command)
520 if working_directory and sys.version_info < (3, 7):
521 working_directory = os.fspath(working_directory)
520522
521523 p = subprocess.Popen(
522524 command,
523525 shell=False,
524 cwd=_path_resolve(working_directory),
526 cwd=working_directory,
525527 env=env,
526528 stdin=stdin_pipe,
527529 stdout=subprocess.PIPE,
00 bump2version==1.0.0
11 coverage==5.3
22 flake8==3.8.3
3 mock==3.0.5
43 pip==20.2.3
54 pytest==6.1.0
65 Sphinx==3.2.1
1212
1313 setup_requirements = []
1414
15 test_requirements = ["mock", "pytest"]
15 test_requirements = ["pytest"]
1616
1717 setup(
1818 author="Markus Gerstel",
2424 "Natural Language :: English",
2525 "Operating System :: OS Independent",
2626 "Programming Language :: Python :: 3",
27 "Programming Language :: Python :: 3.5",
2827 "Programming Language :: Python :: 3.6",
2928 "Programming Language :: Python :: 3.7",
3029 "Programming Language :: Python :: 3.8",
3938 keywords="procrunner",
4039 name="procrunner",
4140 packages=find_packages(include=["procrunner"]),
42 python_requires=">=3.5",
41 python_requires=">=3.6",
4342 setup_requires=setup_requirements,
4443 test_suite="tests",
4544 tests_require=test_requirements,
00 import copy
1 import mock
1 from unittest import mock
22 import os
3 import pathlib
34 import procrunner
45 import pytest
56 import sys
8586 timeout=0.5,
8687 callback_stdout=mock.sentinel.callback_stdout,
8788 callback_stderr=mock.sentinel.callback_stderr,
88 working_directory=mock.sentinel.cwd,
89 working_directory=pathlib.Path("somecwd"),
8990 raise_timeout_exception=True,
9091 )
9192
9293 assert mock_subprocess.Popen.called
9394 assert mock_subprocess.Popen.call_args[1]["env"] == os.environ
94 assert mock_subprocess.Popen.call_args[1]["cwd"] == mock.sentinel.cwd
95 assert mock_subprocess.Popen.call_args[1]["cwd"] in (
96 pathlib.Path("somecwd"),
97 "somecwd",
98 )
9599 mock_streamreader.assert_has_calls(
96100 [
97101 mock.call(
5757
5858
5959 @pytest.mark.skipif(sys.platform != "win32", reason="windows specific test only")
60 def test_name_resolution_for_complex_cases(tmpdir):
61 tmpdir.chdir()
62
60 def test_name_resolution_for_complex_cases(tmp_path):
6361 bat = "simple_bat_extension"
6462 cmd = "simple_cmd_extension"
6563 exe = "simple_exe_extension"
6664 dotshort = "more_complex_filename_with_a.dot"
6765 dotlong = "more_complex_filename.withadot"
6866
69 (tmpdir / bat + ".bat").ensure()
70 (tmpdir / cmd + ".cmd").ensure()
71 (tmpdir / exe + ".exe").ensure()
72 (tmpdir / dotshort + ".bat").ensure()
73 (tmpdir / dotlong + ".cmd").ensure()
67 (tmp_path / (bat + ".bat")).touch()
68 (tmp_path / (cmd + ".cmd")).touch()
69 (tmp_path / (exe + ".exe")).touch()
70 (tmp_path / (dotshort + ".bat")).touch()
71 (tmp_path / (dotlong + ".cmd")).touch()
7472
7573 def is_valid(command):
7674 assert len(command) == 1
77 assert os.path.exists(command[0])
75 assert os.path.exists(tmp_path / command[0])
7876
79 is_valid(procrunner._windows_resolve([bat]))
80 is_valid(procrunner._windows_resolve([cmd]))
81 is_valid(procrunner._windows_resolve([exe]))
82 is_valid(procrunner._windows_resolve([dotshort]))
83 is_valid(procrunner._windows_resolve([dotlong]))
77 is_valid(procrunner._windows_resolve([bat], path=os.fspath(tmp_path)))
78 is_valid(procrunner._windows_resolve([cmd], path=os.fspath(tmp_path)))
79 is_valid(procrunner._windows_resolve([exe], path=os.fspath(tmp_path)))
80 is_valid(procrunner._windows_resolve([dotshort], path=os.fspath(tmp_path)))
81 is_valid(procrunner._windows_resolve([dotlong], path=os.fspath(tmp_path)))
3939 assert err == ""
4040
4141
42 def test_running_wget(tmpdir):
43 tmpdir.chdir()
42 def test_running_wget(tmp_path):
4443 command = ["wget", "https://www.google.com", "-O", "-"]
4544 try:
46 result = procrunner.run(command)
45 result = procrunner.run(command, working_directory=tmp_path)
4746 except OSError as e:
4847 if e.errno == 2:
4948 pytest.skip("wget not available")
5352 assert b"google" in result.stdout
5453
5554
56 def test_path_object_resolution(tmpdir):
55 def test_path_object_resolution(tmp_path):
5756 sentinel_value = b"sentinel"
58 tmpdir.join("tempfile").write(sentinel_value)
59 tmpdir.join("reader.py").write("with open('tempfile') as fh:\n print(fh.read())")
57 tmp_path.joinpath("tempfile").write_bytes(sentinel_value)
58 tmp_path.joinpath("reader.py").write_text(
59 "with open('tempfile') as fh:\n print(fh.read())"
60 )
6061 assert "LEAK_DETECTOR" not in os.environ
6162 result = procrunner.run(
62 [sys.executable, tmpdir.join("reader.py")],
63 [sys.executable, tmp_path / "reader.py"],
6364 environment_override={"PYTHONHASHSEED": "random", "LEAK_DETECTOR": "1"},
64 working_directory=tmpdir,
65 working_directory=tmp_path,
6566 )
6667 assert result.returncode == 0
6768 assert not result.stderr
00 [tox]
1 envlist = py35, py36, py37, py38, py39, flake8
1 envlist = py36, py37, py38, py39, flake8
22
33 [travis]
44 python =
66 3.8: py38
77 3.7: py37
88 3.6: py36
9 3.5: py35
109
1110 [testenv:azure]
1211 basepython = python