Commit bd078d08 authored by Jason Madden's avatar Jason Madden Committed by GitHub

Merge pull request #1054 from gevent/add_36_tests

Add and fix test_ftplib.py and test_asyncore.py for 3.6. 
parents 4f9bb1d9 498ed28f
...@@ -156,6 +156,14 @@ ...@@ -156,6 +156,14 @@
``filename`` attribute set. ``filename`` attribute set.
- The :class:`threading.Timer` class is now monkey-patched and can - The :class:`threading.Timer` class is now monkey-patched and can
be joined. be joined.
- :meth:`gevent.ssl.SSLSocket.unwrap` behaves more like the standard
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) 1.2.2 (2017-06-05)
================== ==================
......
...@@ -5,9 +5,9 @@ internal gevent python 2/python 3 bridges. Not for external use. ...@@ -5,9 +5,9 @@ internal gevent python 2/python 3 bridges. Not for external use.
from __future__ import print_function, absolute_import, division from __future__ import print_function, absolute_import, division
import sys import sys
PY2 = sys.version_info[0] == 2 PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] >= 3 PY3 = sys.version_info[0] >= 3
PYPY = hasattr(sys, 'pypy_version_info') PYPY = hasattr(sys, 'pypy_version_info')
...@@ -19,12 +19,14 @@ if PY3: ...@@ -19,12 +19,14 @@ if PY3:
string_types = (str,) string_types = (str,)
integer_types = (int,) integer_types = (int,)
text_type = str text_type = str
native_path_types = (str, bytes)
else: else:
import __builtin__ # pylint:disable=import-error import __builtin__ # pylint:disable=import-error
string_types = (__builtin__.basestring,) string_types = (__builtin__.basestring,)
text_type = __builtin__.unicode text_type = __builtin__.unicode
integer_types = (int, __builtin__.long) integer_types = (int, __builtin__.long)
native_path_types = string_types
## Exceptions ## Exceptions
...@@ -47,3 +49,42 @@ else: ...@@ -47,3 +49,42 @@ else:
iteritems = dict.iteritems # python 3: pylint:disable=no-member iteritems = dict.iteritems # python 3: pylint:disable=no-member
itervalues = dict.itervalues # python 3: pylint:disable=no-member itervalues = dict.itervalues # python 3: pylint:disable=no-member
xrange = __builtin__.xrange 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'
...@@ -142,6 +142,7 @@ class SSLSocket(socket): ...@@ -142,6 +142,7 @@ class SSLSocket(socket):
server_hostname=None, server_hostname=None,
_session=None, # 3.6 _session=None, # 3.6
_context=None): _context=None):
# pylint:disable=too-many-locals,too-many-statements,too-many-branches # pylint:disable=too-many-locals,too-many-statements,too-many-branches
if _context: if _context:
self._context = _context self._context = _context
...@@ -513,22 +514,32 @@ class SSLSocket(socket): ...@@ -513,22 +514,32 @@ class SSLSocket(socket):
s = self._sslobj.shutdown() s = self._sslobj.shutdown()
break break
except SSLWantReadError: except SSLWantReadError:
# Callers of this method expect to get a socket
# back, so we can't simply return 0, we have
# to let these be raised
if self.timeout == 0.0: if self.timeout == 0.0:
return 0 raise
self._wait(self._read_event) self._wait(self._read_event)
except SSLWantWriteError: except SSLWantWriteError:
if self.timeout == 0.0: if self.timeout == 0.0:
return 0 raise
self._wait(self._write_event) self._wait(self._write_event)
self._sslobj = None self._sslobj = None
# The return value of shutting down the SSLObject is the # The return value of shutting down the SSLObject is the
# original wrapped socket, i.e., _contextawaresock. But that # original wrapped socket passed to _wrap_socket, i.e.,
# object doesn't have the gevent wrapper around it so it can't # _contextawaresock. But that object doesn't have the
# be used. We have to wrap it back up with a gevent wrapper. # gevent wrapper around it so it can't be used. We have to
sock = socket(family=s.family, type=s.type, proto=s.proto, fileno=s.fileno()) # wrap it back up with a gevent wrapper.
s.detach() assert s is self._sock
return sock # In the stdlib, SSLSocket subclasses socket.socket and passes itself
# to _wrap_socket, so it gets itself back. We can't do that, we have to
# pass our subclass of _socket.socket, _contextawaresock.
# So ultimately we should return ourself.
# See test_ftplib.py:TestTLS_FTPClass.test_ccc
return self
else: else:
raise ValueError("No SSL wrapper around " + str(self)) raise ValueError("No SSL wrapper around " + str(self))
......
...@@ -553,7 +553,10 @@ class SSLSocket(socket): ...@@ -553,7 +553,10 @@ class SSLSocket(socket):
if self._sslobj: if self._sslobj:
s = self._sslobj_shutdown() s = self._sslobj_shutdown()
self._sslobj = None self._sslobj = None
return socket(_sock=s) # match _ssl2; critical to drop/reuse here on PyPy # match _ssl2; critical to drop/reuse here on PyPy
# XXX: _ssl3 returns an SSLSocket. Is that what the standard lib does on
# Python 2? Should we do that?
return socket(_sock=s)
else: else:
raise ValueError("No SSL wrapper around " + str(self)) raise ValueError("No SSL wrapper around " + str(self))
......
...@@ -44,6 +44,7 @@ from gevent.hub import get_hub, linkproxy, sleep, getcurrent ...@@ -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 integer_types, string_types, xrange
from gevent._compat import PY3 from gevent._compat import PY3
from gevent._compat import reraise from gevent._compat import reraise
from gevent._compat import fspath
from gevent._util import _NONE from gevent._util import _NONE
from gevent._util import copy_globals from gevent._util import copy_globals
from gevent.fileobject import FileObject from gevent.fileobject import FileObject
...@@ -386,6 +387,11 @@ class Popen(object): ...@@ -386,6 +387,11 @@ class Popen(object):
.. versionchanged:: 1.2a1 .. versionchanged:: 1.2a1
Instances now save the ``args`` attribute under Python 2.7. Previously this was Instances now save the ``args`` attribute under Python 2.7. Previously this was
restricted to Python 3. 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. # The value returned from communicate() when there was nothing to read.
...@@ -554,6 +560,10 @@ class Popen(object): ...@@ -554,6 +560,10 @@ class Popen(object):
self.stderr = FileObject(errread, 'rb', bufsize) self.stderr = FileObject(errread, 'rb', bufsize)
self._closed_child_pipe_fds = False 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: try:
self._execute_child(args, executable, preexec_fn, close_fds, self._execute_child(args, executable, preexec_fn, close_fds,
pass_fds, cwd, env, universal_newlines, pass_fds, cwd, env, universal_newlines,
......
This diff is collapsed.
This diff is collapsed.
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