Codebase list python-procrunner / 0d84dec
Fix merge conflicts Markus Gerstel 3 years ago
8 changed file(s) with 85 addition(s) and 58 deletion(s). Raw diff Collapse all Expand all
11 History
22 =======
33
4 2.2.0 (????-??-??)
4 2.3.0 (????-??-??)
55 ------------------
6 * Python 3.6+ only, support for Python 3.5 has been dropped
67
7 * Python 3.6+ only, support for Python 3.5 has been dropped
8 2.2.0 (2020-09-07)
9 ------------------
10 * Calling the run() function with unnamed arguments (other than the command
11 list as the first argument) is now deprecated. As a number of arguments
12 will be removed in a future version the use of unnamed arguments will
13 cause future confusion. `Use explicit keyword arguments instead (#62). <https://github.com/DiamondLightSource/python-procrunner/pull/62>`_
14 * `The run() function debug argument has been deprecated (#63). <https://github.com/DiamondLightSource/python-procrunner/pull/63>`_
15 This is only used to debug the NonBlockingStream* classes. Those are due
16 to be replaced in a future release, so the argument will no longer serve
17 a purpose. Debugging information remains available via standard logging
18 mechanisms.
19 * Final version supporting Python 3.5
820
921 2.1.0 (2020-09-05)
1022 ------------------
11
1223 * `Deprecated array access on the return object (#60). <https://github.com/DiamondLightSource/python-procrunner/pull/60>`_
1324 The return object will become a subprocess.CompletedProcess in a future
1425 release, which no longer allows array-based access. For a translation table
1526 of array elements to attributes please see the pull request linked above.
16 * Add a `new parameter 'raise_timeout_exceptions' (#61). <https://github.com/DiamondLightSource/python-procrunner/pull/61>`_
27 * Add a `new parameter 'raise_timeout_exception' (#61). <https://github.com/DiamondLightSource/python-procrunner/pull/61>`_
1728 When set to 'True' a subprocess.TimeoutExpired exception is raised when the
1829 process runtime exceeds the timeout threshold. This defaults to 'False' and
1930 will be set to 'True' in a future release.
20 * Final version supporting Python 3.5
2131
2232 2.0.0 (2020-06-24)
2333 ------------------
24
2534 * Python 3.5+ only, support for Python 2.7 has been dropped
2635 * Deprecated function alias run_process() has been removed
2736 * Fixed a stability issue on Windows
2837
2938 1.1.0 (2019-11-04)
3039 ------------------
31
3240 * Add Python 3.8 support, drop Python 3.4 support
3341
3442 1.0.2 (2019-05-20)
3543 ------------------
36
3744 * Stop environment override variables leaking into the process environment
3845
3946 1.0.1 (2019-04-16)
4047 ------------------
41
4248 * Minor fixes on the return object (implement equality,
4349 mark as unhashable)
4450
4551 1.0.0 (2019-03-25)
4652 ------------------
47
4853 * Support file system path objects (PEP-519) in arguments
4954 * Change the return object to make it similar to
5055 subprocess.CompletedProcess, introduced with Python 3.5+
5156
5257 0.9.1 (2019-02-22)
5358 ------------------
54
5559 * Have deprecation warnings point to correct code locations
5660
5761 0.9.0 (2018-12-07)
5862 ------------------
59
6063 * Trap UnicodeEncodeError when printing output. Offending characters
6164 are replaced and a warning is logged once. Hints at incorrectly set
6265 PYTHONIOENCODING.
6366
6467 0.8.1 (2018-12-04)
6568 ------------------
66
6769 * Fix a few deprecation warnings
6870
6971 0.8.0 (2018-10-09)
7072 ------------------
71
7273 * Add parameter working_directory to set the working directory
7374 of the subprocess
7475
7576 0.7.2 (2018-10-05)
7677 ------------------
77
7878 * Officially support Python 3.7
7979
8080 0.7.1 (2018-09-03)
8181 ------------------
82
8382 * Accept environment variable overriding with numeric values.
8483
8584 0.7.0 (2018-05-13)
8685 ------------------
87
8886 * Unicode fixes. Fix crash on invalid UTF-8 input.
8987 * Clarify that stdout/stderr values are returned as bytestrings.
9088 * Callbacks receive the data decoded as UTF-8 unicode strings
9492
9593 0.6.1 (2018-05-02)
9694 ------------------
97
9895 * Maintenance release to add some tests for executable resolution.
9996
10097 0.6.0 (2018-05-02)
10198 ------------------
102
10399 * Fix Win32 API executable resolution for commands containing a dot ('.') in
104100 addition to a file extension (say '.bat').
105101
106102 0.5.1 (2018-04-27)
107103 ------------------
108
109104 * Fix Win32API dependency installation on Windows.
110105
111106 0.5.0 (2018-04-26)
112107 ------------------
113
114108 * New keyword 'win32resolve' which only takes effect on Windows and is enabled
115109 by default. This causes procrunner to call the Win32 API FindExecutable()
116110 function to try and lookup non-.exe files with the corresponding name. This
119113
120114 0.4.0 (2018-04-23)
121115 ------------------
122
123116 * Python 2.7 support on Windows. Python3 not yet supported on Windows.
124117
125118 0.3.0 (2018-04-17)
126119 ------------------
127
128120 * run_process() renamed to run()
129121 * Python3 compatibility fixes
130122
131123 0.2.0 (2018-03-12)
132124 ------------------
133
134125 * Procrunner is now Python3 3.3-3.6 compatible.
135126
136127 0.1.0 (2018-03-12)
137128 ------------------
138
139129 * First release on PyPI.
4747
4848 # General information about the project.
4949 project = "ProcRunner"
50 copyright = "2020, Markus Gerstel"
51 author = "Markus Gerstel"
50 copyright = "2020, Diamond Light Source"
51 author = "Diamond Light Source - Scientific Software"
5252
5353 # The version info for the project you're documenting, acts as replacement
5454 # for |version| and |release|, also used in various other places throughout
127127 (
128128 master_doc,
129129 "procrunner.tex",
130 "ProcRunner Documentation",
131 "Markus Gerstel",
130 "procrunner Documentation",
131 "Diamond Light Source - Scientific Software",
132132 "manual",
133133 )
134134 ]
138138
139139 # One entry per manual page. List of tuples
140140 # (source start file, name, description, authors, manual section).
141 man_pages = [(master_doc, "procrunner", "ProcRunner Documentation", [author], 1)]
141 man_pages = [(master_doc, "procrunner", "procrunner Documentation", [author], 1)]
142142
143143
144144 # -- Options for Texinfo output ----------------------------------------
150150 (
151151 master_doc,
152152 "procrunner",
153 "ProcRunner Documentation",
153 "procrunner Documentation",
154154 author,
155155 "procrunner",
156 "One line description of project.",
156 "Versatile utility function to run external processes",
157157 "Miscellaneous",
158158 )
159159 ]
00 import codecs
1 import functools
12 import io
23 import logging
34 import os
5152
5253 __author__ = """Markus Gerstel"""
5354 __email__ = "scientificsoftware@diamond.ac.uk"
54 __version__ = "2.1.0"
55 __version__ = "2.2.0"
5556
5657 logger = logging.getLogger("procrunner")
5758 logger.addHandler(logging.NullHandler())
208209 self._buffer = data
209210 self._buffer_len = len(data)
210211 self._buffer_pos = 0
211 self._debug = debug
212212 self._max_block_len = 4096
213213 self._stream = stream
214214 self._terminated = False
412412 self._extras.update(dictionary)
413413
414414
415 def _deprecate_argument_calling(f):
416 @functools.wraps(f)
417 def wrapper(*args, **kwargs):
418 if len(args) > 1:
419 warnings.warn(
420 "Calling procrunner.run() with unnamed arguments (apart from "
421 "the command) is deprecated. Use keyword arguments instead.",
422 DeprecationWarning,
423 stacklevel=2,
424 )
425 return f(*args, **kwargs)
426
427 return wrapper
428
429
430 @_deprecate_argument_calling
415431 def run(
416432 command,
417433 timeout=None,
418 debug=False,
434 debug=None,
419435 stdin=None,
420436 print_stdout=True,
421437 print_stderr=True,
435451
436452 :param array command: Command line to be run, specified as array.
437453 :param timeout: Terminate program execution after this many seconds.
438 :param boolean debug: Enable further debug messages.
454 :param boolean debug: Enable further debug messages. (deprecated)
439455 :param stdin: Optional bytestring that is passed to command stdin.
440456 :param boolean print_stdout: Pass stdout through to sys.stdout.
441457 :param boolean print_stderr: Pass stderr through to sys.stderr.
469485 else:
470486 assert sys.platform != "win32", "stdin argument not supported on Windows"
471487 stdin_pipe = subprocess.PIPE
488 if debug is not None:
489 warnings.warn(
490 "Use of the debug parameter is deprecated", DeprecationWarning, stacklevel=3
491 )
472492
473493 start_time = timeit.default_timer()
474494 if timeout is not None:
477497 warnings.warn(
478498 "Using procrunner with timeout and without raise_timeout_exception set is deprecated",
479499 DeprecationWarning,
480 stacklevel=2,
500 stacklevel=3,
481501 )
482502
483503 if environment is not None:
00 bump2version==1.0.0
1 coverage==5.2.1
1 coverage==5.3
22 flake8==3.8.3
33 mock==3.0.5
4 pip==20.2.2
5 pytest==6.0.1
4 pip==20.2.3
5 pytest==6.1.0
66 Sphinx==3.2.1
7 tox==3.19.0
7 tox==3.20.0
88 twine==1.15.0
99 wheel==0.35.1
00 [bumpversion]
1 current_version = 2.1.0
1 current_version = 2.2.0
22 commit = True
33 tag = True
44
4343 test_suite="tests",
4444 tests_require=test_requirements,
4545 url="https://github.com/DiamondLightSource/python-procrunner",
46 version="2.1.0",
46 version="2.2.0",
4747 zip_safe=False,
4848 )
2121
2222 with pytest.raises(RuntimeError):
2323 with pytest.warns(DeprecationWarning, match="timeout"):
24 procrunner.run(task, -1, False)
24 procrunner.run(task, timeout=-1, debug=False)
2525
2626 assert mock_subprocess.Popen.called
2727 assert mock_process.terminate.called
4242 task = ["___"]
4343
4444 with pytest.raises(RuntimeError):
45 procrunner.run(task, -1, False, raise_timeout_exception=True)
45 procrunner.run(task, timeout=-1, raise_timeout_exception=True)
4646
4747 assert mock_subprocess.Popen.called
4848 assert mock_process.terminate.called
8383
8484 actual = procrunner.run(
8585 command,
86 0.5,
87 False,
86 timeout=0.5,
8887 callback_stdout=mock.sentinel.callback_stdout,
8988 callback_stderr=mock.sentinel.callback_stderr,
9089 working_directory=pathlib.Path("somecwd"),
102101 mock.call(
103102 stream_stdout,
104103 output=mock.ANY,
105 debug=mock.ANY,
104 debug=None,
106105 notify=mock.ANY,
107106 callback=mock.sentinel.callback_stdout,
108107 ),
109108 mock.call(
110109 stream_stderr,
111110 output=mock.ANY,
112 debug=mock.ANY,
111 debug=None,
113112 notify=mock.ANY,
114113 callback=mock.sentinel.callback_stderr,
115114 ),
131130 def test_default_process_environment_is_parent_environment(mock_subprocess):
132131 mock_subprocess.Popen.side_effect = NotImplementedError() # cut calls short
133132 with pytest.raises(NotImplementedError):
134 procrunner.run([mock.Mock()], -1, False, raise_timeout_exception=True)
133 procrunner.run([mock.Mock()], timeout=-1, raise_timeout_exception=True)
135134 assert mock_subprocess.Popen.call_args[1]["env"] == os.environ
135
136
137 @mock.patch("procrunner.subprocess")
138 def test_using_debug_parameter_raises_warning(mock_subprocess):
139 mock_subprocess.Popen.side_effect = NotImplementedError() # cut calls short
140 with pytest.warns(DeprecationWarning, match="debug"):
141 with pytest.raises(NotImplementedError):
142 procrunner.run([mock.Mock()], debug=True)
143 with pytest.warns(DeprecationWarning, match="debug"):
144 with pytest.raises(NotImplementedError):
145 procrunner.run([mock.Mock()], debug=False)
136146
137147
138148 @mock.patch("procrunner.subprocess")
143153 with pytest.raises(NotImplementedError):
144154 procrunner.run(
145155 [mock.Mock()],
146 -1,
147 False,
156 timeout=-1,
148157 environment=copy.copy(mock_env),
149158 raise_timeout_exception=True,
150159 )
160169 with pytest.raises(NotImplementedError):
161170 procrunner.run(
162171 [mock.Mock()],
163 -1,
164 False,
172 timeout=-1,
165173 environment=copy.copy(mock_env1),
166174 environment_override=copy.copy(mock_env2),
167175 raise_timeout_exception=True,
178186 with pytest.raises(NotImplementedError):
179187 procrunner.run(
180188 [mock.Mock()],
181 -1,
182 False,
189 timeout=-1,
183190 environment_override=copy.copy(mock_env2),
184191 raise_timeout_exception=True,
185192 )
207214 with pytest.raises(NotImplementedError):
208215 procrunner.run(
209216 [mock.Mock()],
210 -1,
211 False,
217 timeout=-1,
212218 environment_override={
213219 random_environment_variable: "X" + random_environment_value
214220 },
116116 assert te.value.stderr == b""
117117 assert te.value.timeout == 0.1
118118 assert te.value.cmd == command
119
120
121 def test_argument_deprecation(tmp_path):
122 with pytest.warns(DeprecationWarning, match="keyword arguments"):
123 result = procrunner.run(
124 [sys.executable, "-V"],
125 None,
126 working_directory=tmp_path,
127 )
128 assert not result.returncode
129 assert result.stderr or result.stdout