Codebase list python-procrunner / 3feaaed
Merge pull request #69 from DiamondLightSource/py36updates Drop Python 3.5 support Markus Gerstel authored 3 years ago GitHub committed 3 years ago
13 changed file(s) with 41 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"
272272 return obj
273273
274274
275 def _windows_resolve(command):
275 def _windows_resolve(command, path=None):
276276 """
277277 Try and find the full path and file extension of the executable to run.
278278 This is so that e.g. calls to 'somescript' will point at 'somescript.cmd'
287287 if not command or not isinstance(command[0], str):
288288 return command
289289
290 found_executable = shutil.which(command[0])
290 found_executable = shutil.which(command[0], path=path)
291291 if found_executable:
292292 logger.debug("Resolved %s as %s", command[0], found_executable)
293293 return (found_executable, *command[1:])
296296 # Special case. shutil.which may not detect file extensions if a full
297297 # path is given, so try to resolve the executable explicitly
298298 for extension in os.getenv("PATHEXT").split(os.pathsep):
299 found_executable = shutil.which(command[0] + extension)
299 found_executable = shutil.which(command[0] + extension, path=path)
300300 if found_executable:
301301 return (found_executable, *command[1:])
302302
332332 if key in self._extras:
333333 return self._extras[key]
334334 if not hasattr(self, key):
335 raise KeyError("Unknown attribute {key}".format(key=key))
335 raise KeyError(f"Unknown attribute {key}")
336336 return getattr(self, key)
337337
338338 def __eq__(self, other):
515515 command = tuple(_path_resolve(part) for part in command)
516516 if win32resolve and sys.platform == "win32":
517517 command = _windows_resolve(command)
518 if working_directory and sys.version_info < (3, 7):
519 working_directory = os.fspath(working_directory)
518520
519521 p = subprocess.Popen(
520522 command,
521523 shell=False,
522 cwd=_path_resolve(working_directory),
524 cwd=working_directory,
523525 env=env,
524526 stdin=stdin_pipe,
525527 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("print(open('tempfile').read())")
57 tmp_path.joinpath("tempfile").write_bytes(sentinel_value)
58 tmp_path.joinpath("reader.py").write_text("print(open('tempfile').read())")
6059 assert "LEAK_DETECTOR" not in os.environ
6160 result = procrunner.run(
62 [sys.executable, tmpdir.join("reader.py")],
61 [sys.executable, tmp_path / "reader.py"],
6362 environment_override={"PYTHONHASHSEED": "random", "LEAK_DETECTOR": "1"},
64 working_directory=tmpdir,
63 working_directory=tmp_path,
6564 )
6665 assert result.returncode == 0
6766 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