Commit 498ed28f authored by Jason Madden's avatar Jason Madden

Fix test_subprocess.py on Windows 3.6 by adding path-like support.

parent 897d20ff
......@@ -160,6 +160,10 @@
library, including returning a SSLSocket and allowing certain
timeout-related SSL errors to propagate. The added standard
library tests ``test_ftplib.py`` now passes.
- :class:`gevent.subprocess.Popen` accepts a "path-like object" for
the *cwd* parameter on all platforms. Previously this only worked
on POSIX platforms under Python 3.6. Now it also works on Windows under
Python 3.6 (as expected) and is backported to all previous versions.
1.2.2 (2017-06-05)
==================
......
......@@ -5,9 +5,9 @@ internal gevent python 2/python 3 bridges. Not for external use.
from __future__ import print_function, absolute_import, division
import sys
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] >= 3
PYPY = hasattr(sys, 'pypy_version_info')
......@@ -19,12 +19,14 @@ if PY3:
string_types = (str,)
integer_types = (int,)
text_type = str
native_path_types = (str, bytes)
else:
import __builtin__ # pylint:disable=import-error
string_types = (__builtin__.basestring,)
text_type = __builtin__.unicode
integer_types = (int, __builtin__.long)
native_path_types = string_types
## Exceptions
......@@ -47,3 +49,42 @@ else:
iteritems = dict.iteritems # python 3: pylint:disable=no-member
itervalues = dict.itervalues # python 3: pylint:disable=no-member
xrange = __builtin__.xrange
# fspath from 3.6 os.py, but modified to raise the same exceptions as the
# real native implementation.
# Define for testing
def _fspath(path):
"""
Return the path representation of a path-like object.
If str or bytes is passed in, it is returned unchanged. Otherwise the
os.PathLike interface is used to get the path representation. If the
path representation is not str or bytes, TypeError is raised. If the
provided path is not str, bytes, or os.PathLike, TypeError is raised.
"""
if isinstance(path, native_path_types):
return path
# Work from the object's type to match method resolution of other magic
# methods.
path_type = type(path)
try:
path_type_fspath = path_type.__fspath__
except AttributeError:
raise TypeError("expected str, bytes or os.PathLike object, "
"not " + path_type.__name__)
path_repr = path_type_fspath(path)
if isinstance(path_repr, native_path_types):
return path_repr
raise TypeError("expected {}.__fspath__() to return str or bytes, "
"not {}".format(path_type.__name__,
type(path_repr).__name__))
try:
from os import fspath # pylint: disable=unused-import,no-name-in-module
except ImportError:
# if not available, use the Python version as transparently as
# possible
fspath = _fspath
fspath.__name__ = 'fspath'
......@@ -44,6 +44,7 @@ from gevent.hub import get_hub, linkproxy, sleep, getcurrent
from gevent._compat import integer_types, string_types, xrange
from gevent._compat import PY3
from gevent._compat import reraise
from gevent._compat import fspath
from gevent._util import _NONE
from gevent._util import copy_globals
from gevent.fileobject import FileObject
......@@ -386,6 +387,11 @@ class Popen(object):
.. versionchanged:: 1.2a1
Instances now save the ``args`` attribute under Python 2.7. Previously this was
restricted to Python 3.
.. versionchanged:: 1.3a1
Accept "path-like" objects for the *cwd* parameter on all platforms.
This was added to Python 3.6. Previously with gevent, it only worked
on POSIX platforms on 3.6.
"""
# The value returned from communicate() when there was nothing to read.
......@@ -554,6 +560,10 @@ class Popen(object):
self.stderr = FileObject(errread, 'rb', bufsize)
self._closed_child_pipe_fds = False
# Convert here for the sake of all platforms. os.chdir accepts
# path-like objects natively under 3.6, but CreateProcess
# doesn't.
cwd = fspath(cwd) if cwd is not None else None
try:
self._execute_child(args, executable, preexec_fn, close_fds,
pass_fds, cwd, env, universal_newlines,
......
from __future__ import absolute_import, print_function, division
import os
import unittest
class TestFSPath(unittest.TestCase):
def setUp(self):
self.__path = None
def __fspath__(self):
if self.__path is not None:
return self.__path
raise AttributeError("Accessing path data")
def _callFUT(self, arg):
from gevent._compat import _fspath
return _fspath(arg)
def test_text(self):
s = u'path'
self.assertIs(s, self._callFUT(s))
def test_bytes(self):
s = b'path'
self.assertIs(s, self._callFUT(s))
def test_None(self):
with self.assertRaises(TypeError):
self._callFUT(None)
def test_working_path(self):
self.__path = u'text'
self.assertIs(self.__path, self._callFUT(self))
self.__path = b'bytes'
self.assertIs(self.__path, self._callFUT(self))
def test_failing_path_AttributeError(self):
self.assertIsNone(self.__path)
with self.assertRaises(AttributeError):
self._callFUT(self)
def test_fspath_non_str(self):
self.__path = object()
with self.assertRaises(TypeError):
self._callFUT(self)
@unittest.skipUnless(hasattr(os, 'fspath'), "Tests native os.fspath")
class TestNativeFSPath(TestFSPath):
def _callFUT(self, arg):
return os.fspath(arg)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment