Skip to content

Commit

Permalink
Merge pull request #69 from DiamondLightSource/py36updates
Browse files Browse the repository at this point in the history
Drop Python 3.5 support
  • Loading branch information
Anthchirp committed Oct 29, 2020
2 parents 254fdd2 + 79c2516 commit 3feaaed
Show file tree
Hide file tree
Showing 13 changed files with 41 additions and 76 deletions.
6 changes: 0 additions & 6 deletions .azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ jobs:
vmImage: ubuntu-latest
strategy:
matrix:
python35:
PYTHON_VERSION: 3.5
python36:
PYTHON_VERSION: 3.6
python37:
Expand All @@ -43,8 +41,6 @@ jobs:
vmImage: macOS-latest
strategy:
matrix:
python35:
PYTHON_VERSION: 3.5
python36:
PYTHON_VERSION: 3.6
python37:
Expand All @@ -60,8 +56,6 @@ jobs:
vmImage: windows-latest
strategy:
matrix:
python35:
PYTHON_VERSION: 3.5
python36:
PYTHON_VERSION: 3.6
python37:
Expand Down
2 changes: 1 addition & 1 deletion .azure-pipelines/syntax-validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
continue
filename = os.path.normpath(os.path.join(base, f))
try:
with open(filename, "r") as fh:
with open(filename) as fh:
ast.parse(fh.read())
except SyntaxError as se:
failures += 1
Expand Down
7 changes: 0 additions & 7 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,3 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "monthly"
ignore:
- dependency-name: "mock"
# mock 4 requires Python 3.6+
versions: [">=4"]
- dependency-name: "twine"
# twine 2 requires Python 3.6+
versions: [">=2"]
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ matrix:
- python: 3.8
- python: 3.7
- python: 3.6
- python: 3.5
- os: osx
language: generic
env: CONDA=3.8 TOXENV=py38
Expand All @@ -22,9 +21,6 @@ matrix:
- os: osx
language: generic
env: CONDA=3.6 TOXENV=py36
- os: osx
language: generic
env: CONDA=3.5 TOXENV=py35

allow_failures:
- env: OPTIONAL=1
Expand Down
24 changes: 4 additions & 20 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
History
=======

2.3.0 (????-??-??)
------------------
* Python 3.6+ only, support for Python 3.5 has been dropped

2.2.0 (2020-09-07)
------------------
* Calling the run() function with unnamed arguments (other than the command
Expand All @@ -28,70 +32,58 @@ History

2.0.0 (2020-06-24)
------------------

* Python 3.5+ only, support for Python 2.7 has been dropped
* Deprecated function alias run_process() has been removed
* Fixed a stability issue on Windows

1.1.0 (2019-11-04)
------------------

* Add Python 3.8 support, drop Python 3.4 support

1.0.2 (2019-05-20)
------------------

* Stop environment override variables leaking into the process environment

1.0.1 (2019-04-16)
------------------

* Minor fixes on the return object (implement equality,
mark as unhashable)

1.0.0 (2019-03-25)
------------------

* Support file system path objects (PEP-519) in arguments
* Change the return object to make it similar to
subprocess.CompletedProcess, introduced with Python 3.5+

0.9.1 (2019-02-22)
------------------

* Have deprecation warnings point to correct code locations

0.9.0 (2018-12-07)
------------------

* Trap UnicodeEncodeError when printing output. Offending characters
are replaced and a warning is logged once. Hints at incorrectly set
PYTHONIOENCODING.

0.8.1 (2018-12-04)
------------------

* Fix a few deprecation warnings

0.8.0 (2018-10-09)
------------------

* Add parameter working_directory to set the working directory
of the subprocess

0.7.2 (2018-10-05)
------------------

* Officially support Python 3.7

0.7.1 (2018-09-03)
------------------

* Accept environment variable overriding with numeric values.

0.7.0 (2018-05-13)
------------------

* Unicode fixes. Fix crash on invalid UTF-8 input.
* Clarify that stdout/stderr values are returned as bytestrings.
* Callbacks receive the data decoded as UTF-8 unicode strings
Expand All @@ -101,23 +93,19 @@ History

0.6.1 (2018-05-02)
------------------

* Maintenance release to add some tests for executable resolution.

0.6.0 (2018-05-02)
------------------

* Fix Win32 API executable resolution for commands containing a dot ('.') in
addition to a file extension (say '.bat').

0.5.1 (2018-04-27)
------------------

* Fix Win32API dependency installation on Windows.

0.5.0 (2018-04-26)
------------------

* New keyword 'win32resolve' which only takes effect on Windows and is enabled
by default. This causes procrunner to call the Win32 API FindExecutable()
function to try and lookup non-.exe files with the corresponding name. This
Expand All @@ -126,21 +114,17 @@ History

0.4.0 (2018-04-23)
------------------

* Python 2.7 support on Windows. Python3 not yet supported on Windows.

0.3.0 (2018-04-17)
------------------

* run_process() renamed to run()
* Python3 compatibility fixes

0.2.0 (2018-03-12)
------------------

* Procrunner is now Python3 3.3-3.6 compatible.

0.1.0 (2018-03-12)
------------------

* First release on PyPI.
2 changes: 0 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ environment:
# For Python versions available on Appveyor, see
# http://www.appveyor.com/docs/installed-software#python

- PYTHON: "C:\\Python35"
- PYTHON: "C:\\Python36"
- PYTHON: "C:\\Python37"
- PYTHON: "C:\\Python38"
- PYTHON: "C:\\Python35-x64"
- PYTHON: "C:\\Python36-x64"
- PYTHON: "C:\\Python37-x64"
- PYTHON: "C:\\Python38-x64"
Expand Down
12 changes: 7 additions & 5 deletions procrunner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def _path_resolve(obj):
return obj


def _windows_resolve(command):
def _windows_resolve(command, path=None):
"""
Try and find the full path and file extension of the executable to run.
This is so that e.g. calls to 'somescript' will point at 'somescript.cmd'
Expand All @@ -288,7 +288,7 @@ def _windows_resolve(command):
if not command or not isinstance(command[0], str):
return command

found_executable = shutil.which(command[0])
found_executable = shutil.which(command[0], path=path)
if found_executable:
logger.debug("Resolved %s as %s", command[0], found_executable)
return (found_executable, *command[1:])
Expand All @@ -297,7 +297,7 @@ def _windows_resolve(command):
# Special case. shutil.which may not detect file extensions if a full
# path is given, so try to resolve the executable explicitly
for extension in os.getenv("PATHEXT").split(os.pathsep):
found_executable = shutil.which(command[0] + extension)
found_executable = shutil.which(command[0] + extension, path=path)
if found_executable:
return (found_executable, *command[1:])

Expand Down Expand Up @@ -333,7 +333,7 @@ def __getitem__(self, key):
if key in self._extras:
return self._extras[key]
if not hasattr(self, key):
raise KeyError("Unknown attribute {key}".format(key=key))
raise KeyError(f"Unknown attribute {key}")
return getattr(self, key)

def __eq__(self, other):
Expand Down Expand Up @@ -516,11 +516,13 @@ def run(
command = tuple(_path_resolve(part) for part in command)
if win32resolve and sys.platform == "win32":
command = _windows_resolve(command)
if working_directory and sys.version_info < (3, 7):
working_directory = os.fspath(working_directory)

p = subprocess.Popen(
command,
shell=False,
cwd=_path_resolve(working_directory),
cwd=working_directory,
env=env,
stdin=stdin_pipe,
stdout=subprocess.PIPE,
Expand Down
1 change: 0 additions & 1 deletion requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
bump2version==1.0.0
coverage==5.3
flake8==3.8.3
mock==3.0.5
pip==20.2.3
pytest==6.1.0
Sphinx==3.2.1
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

setup_requirements = []

test_requirements = ["mock", "pytest"]
test_requirements = ["pytest"]

setup(
author="Markus Gerstel",
Expand All @@ -25,7 +25,6 @@
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
Expand All @@ -40,7 +39,7 @@
keywords="procrunner",
name="procrunner",
packages=find_packages(include=["procrunner"]),
python_requires=">=3.5",
python_requires=">=3.6",
setup_requires=setup_requirements,
test_suite="tests",
tests_require=test_requirements,
Expand Down
10 changes: 7 additions & 3 deletions tests/test_procrunner.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import copy
import mock
from unittest import mock
import os
import pathlib
import procrunner
import pytest
import sys
Expand Down Expand Up @@ -86,13 +87,16 @@ def streamreader_processing(*args, **kwargs):
timeout=0.5,
callback_stdout=mock.sentinel.callback_stdout,
callback_stderr=mock.sentinel.callback_stderr,
working_directory=mock.sentinel.cwd,
working_directory=pathlib.Path("somecwd"),
raise_timeout_exception=True,
)

assert mock_subprocess.Popen.called
assert mock_subprocess.Popen.call_args[1]["env"] == os.environ
assert mock_subprocess.Popen.call_args[1]["cwd"] == mock.sentinel.cwd
assert mock_subprocess.Popen.call_args[1]["cwd"] in (
pathlib.Path("somecwd"),
"somecwd",
)
mock_streamreader.assert_has_calls(
[
mock.call(
Expand Down
26 changes: 12 additions & 14 deletions tests/test_procrunner_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,27 +58,25 @@ def test_name_resolution_for_simple_exe():


@pytest.mark.skipif(sys.platform != "win32", reason="windows specific test only")
def test_name_resolution_for_complex_cases(tmpdir):
tmpdir.chdir()

def test_name_resolution_for_complex_cases(tmp_path):
bat = "simple_bat_extension"
cmd = "simple_cmd_extension"
exe = "simple_exe_extension"
dotshort = "more_complex_filename_with_a.dot"
dotlong = "more_complex_filename.withadot"

(tmpdir / bat + ".bat").ensure()
(tmpdir / cmd + ".cmd").ensure()
(tmpdir / exe + ".exe").ensure()
(tmpdir / dotshort + ".bat").ensure()
(tmpdir / dotlong + ".cmd").ensure()
(tmp_path / (bat + ".bat")).touch()
(tmp_path / (cmd + ".cmd")).touch()
(tmp_path / (exe + ".exe")).touch()
(tmp_path / (dotshort + ".bat")).touch()
(tmp_path / (dotlong + ".cmd")).touch()

def is_valid(command):
assert len(command) == 1
assert os.path.exists(command[0])
assert os.path.exists(tmp_path / command[0])

is_valid(procrunner._windows_resolve([bat]))
is_valid(procrunner._windows_resolve([cmd]))
is_valid(procrunner._windows_resolve([exe]))
is_valid(procrunner._windows_resolve([dotshort]))
is_valid(procrunner._windows_resolve([dotlong]))
is_valid(procrunner._windows_resolve([bat], path=os.fspath(tmp_path)))
is_valid(procrunner._windows_resolve([cmd], path=os.fspath(tmp_path)))
is_valid(procrunner._windows_resolve([exe], path=os.fspath(tmp_path)))
is_valid(procrunner._windows_resolve([dotshort], path=os.fspath(tmp_path)))
is_valid(procrunner._windows_resolve([dotlong], path=os.fspath(tmp_path)))
15 changes: 7 additions & 8 deletions tests/test_procrunner_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ def test_decode_invalid_utf8_input(capsys):
assert err == ""


def test_running_wget(tmpdir):
tmpdir.chdir()
def test_running_wget(tmp_path):
command = ["wget", "https://www.google.com", "-O", "-"]
try:
result = procrunner.run(command)
result = procrunner.run(command, working_directory=tmp_path)
except OSError as e:
if e.errno == 2:
pytest.skip("wget not available")
Expand All @@ -54,15 +53,15 @@ def test_running_wget(tmpdir):
assert b"google" in result.stdout


def test_path_object_resolution(tmpdir):
def test_path_object_resolution(tmp_path):
sentinel_value = b"sentinel"
tmpdir.join("tempfile").write(sentinel_value)
tmpdir.join("reader.py").write("print(open('tempfile').read())")
tmp_path.joinpath("tempfile").write_bytes(sentinel_value)
tmp_path.joinpath("reader.py").write_text("print(open('tempfile').read())")
assert "LEAK_DETECTOR" not in os.environ
result = procrunner.run(
[sys.executable, tmpdir.join("reader.py")],
[sys.executable, tmp_path / "reader.py"],
environment_override={"PYTHONHASHSEED": "random", "LEAK_DETECTOR": "1"},
working_directory=tmpdir,
working_directory=tmp_path,
)
assert result.returncode == 0
assert not result.stderr
Expand Down
Loading

0 comments on commit 3feaaed

Please sign in to comment.