[Python-checkins] cpython (merge default -> default): merge heads

benjamin.peterson python-checkins at python.org
Tue Sep 6 18:57:59 EDT 2016


https://hg.python.org/cpython/rev/a882c1981029
changeset:   103162:a882c1981029
parent:      103160:42673b6fc6c5
parent:      103158:1d29d3a1e0c6
user:        Benjamin Peterson <benjamin at python.org>
date:        Tue Sep 06 15:57:48 2016 -0700
summary:
  merge heads

files:
  Doc/c-api/unicode.rst          |   5 +-
  Lib/test/test_os.py            |  64 +++++++++------------
  Misc/NEWS                      |   5 +
  Modules/clinic/posixmodule.c.h |  14 ++--
  Modules/posixmodule.c          |   8 +-
  Objects/unicodeobject.c        |  27 ++++----
  6 files changed, 62 insertions(+), 61 deletions(-)


diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -810,13 +810,16 @@
 
 .. c:function:: int PyUnicode_FSConverter(PyObject* obj, void* result)
 
-   ParseTuple converter: encode :class:`str` objects to :class:`bytes` using
+   ParseTuple converter: encode :class:`str` objects -- obtained directly or
+   through the :class:`os.PathLike` interface -- to :class:`bytes` using
    :c:func:`PyUnicode_EncodeFSDefault`; :class:`bytes` objects are output as-is.
    *result* must be a :c:type:`PyBytesObject*` which must be released when it is
    no longer used.
 
    .. versionadded:: 3.1
 
+   .. versionchanged:: 3.6
+      Accepts a :term:`path-like object`.
 
 To decode file names during argument parsing, the ``"O&"`` converter should be
 used, passing :c:func:`PyUnicode_FSDecoder` as the conversion function:
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -100,6 +100,21 @@
         yield
 
 
+class _PathLike(os.PathLike):
+
+    def __init__(self, path=""):
+        self.path = path
+
+    def __str__(self):
+        return str(self.path)
+
+    def __fspath__(self):
+        if isinstance(self.path, BaseException):
+            raise self.path
+        else:
+            return self.path
+
+
 def create_file(filename, content=b'content'):
     with open(filename, "xb", 0) as fp:
         fp.write(content)
@@ -894,15 +909,7 @@
         self.assertEqual(all[1], self.sub2_tree)
 
     def test_file_like_path(self):
-        class FileLike:
-            def __init__(self, path):
-                self._path = path
-            def __str__(self):
-                return str(self._path)
-            def __fspath__(self):
-                return self._path
-
-        self.test_walk_prune(FileLike(self.walk_path))
+        self.test_walk_prune(_PathLike(self.walk_path))
 
     def test_walk_bottom_up(self):
         # Walk bottom-up.
@@ -2124,7 +2131,8 @@
 
     def test_waitpid(self):
         args = [sys.executable, '-c', 'pass']
-        pid = os.spawnv(os.P_NOWAIT, args[0], args)
+        # Add an implicit test for PyUnicode_FSConverter().
+        pid = os.spawnv(os.P_NOWAIT, _PathLike(args[0]), args)
         status = os.waitpid(pid, 0)
         self.assertEqual(status, (pid, 0))
 
@@ -2833,25 +2841,18 @@
     ]
 
     def test_path_t_converter(self):
-        class PathLike:
-            def __init__(self, path):
-                self.path = path
-
-            def __fspath__(self):
-                return self.path
-
         str_filename = support.TESTFN
         if os.name == 'nt':
             bytes_fspath = bytes_filename = None
         else:
             bytes_filename = support.TESTFN.encode('ascii')
-            bytes_fspath = PathLike(bytes_filename)
-        fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT)
+            bytes_fspath = _PathLike(bytes_filename)
+        fd = os.open(_PathLike(str_filename), os.O_WRONLY|os.O_CREAT)
         self.addCleanup(support.unlink, support.TESTFN)
         self.addCleanup(os.close, fd)
 
-        int_fspath = PathLike(fd)
-        str_fspath = PathLike(str_filename)
+        int_fspath = _PathLike(fd)
+        str_fspath = _PathLike(str_filename)
 
         for name, allow_fd, extra_args, cleanup_fn in self.functions:
             with self.subTest(name=name):
@@ -3205,15 +3206,6 @@
     # if a C version is provided.
     fspath = staticmethod(os.fspath)
 
-    class PathLike:
-        def __init__(self, path=''):
-            self.path = path
-        def __fspath__(self):
-            if isinstance(self.path, BaseException):
-                raise self.path
-            else:
-                return self.path
-
     def test_return_bytes(self):
         for b in b'hello', b'goodbye', b'some/path/and/file':
             self.assertEqual(b, self.fspath(b))
@@ -3224,16 +3216,16 @@
 
     def test_fsencode_fsdecode(self):
         for p in "path/like/object", b"path/like/object":
-            pathlike = self.PathLike(p)
+            pathlike = _PathLike(p)
 
             self.assertEqual(p, self.fspath(pathlike))
             self.assertEqual(b"path/like/object", os.fsencode(pathlike))
             self.assertEqual("path/like/object", os.fsdecode(pathlike))
 
     def test_pathlike(self):
-        self.assertEqual('#feelthegil', self.fspath(self.PathLike('#feelthegil')))
-        self.assertTrue(issubclass(self.PathLike, os.PathLike))
-        self.assertTrue(isinstance(self.PathLike(), os.PathLike))
+        self.assertEqual('#feelthegil', self.fspath(_PathLike('#feelthegil')))
+        self.assertTrue(issubclass(_PathLike, os.PathLike))
+        self.assertTrue(isinstance(_PathLike(), os.PathLike))
 
     def test_garbage_in_exception_out(self):
         vapor = type('blah', (), {})
@@ -3245,14 +3237,14 @@
 
     def test_bad_pathlike(self):
         # __fspath__ returns a value other than str or bytes.
-        self.assertRaises(TypeError, self.fspath, self.PathLike(42))
+        self.assertRaises(TypeError, self.fspath, _PathLike(42))
         # __fspath__ attribute that is not callable.
         c = type('foo', (), {})
         c.__fspath__ = 1
         self.assertRaises(TypeError, self.fspath, c())
         # __fspath__ raises an exception.
         self.assertRaises(ZeroDivisionError, self.fspath,
-                          self.PathLike(ZeroDivisionError()))
+                          _PathLike(ZeroDivisionError()))
 
 # Only test if the C version is provided, otherwise TestPEP519 already tested
 # the pure Python implementation.
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -188,6 +188,11 @@
 
 - Issue #27573: exit message for code.interact is now configurable.
 
+C API
+-----
+
+- Issue #26027: Add support for path-like objects in PyUnicode_FSConverter().
+
 Tests
 -----
 
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -3011,13 +3011,13 @@
     {"waitpid", (PyCFunction)os_waitpid, METH_VARARGS, os_waitpid__doc__},
 
 static PyObject *
-os_waitpid_impl(PyObject *module, Py_intptr_t pid, int options);
+os_waitpid_impl(PyObject *module, intptr_t pid, int options);
 
 static PyObject *
 os_waitpid(PyObject *module, PyObject *args)
 {
     PyObject *return_value = NULL;
-    Py_intptr_t pid;
+    intptr_t pid;
     int options;
 
     if (!PyArg_ParseTuple(args, "" _Py_PARSE_INTPTR "i:waitpid",
@@ -5479,13 +5479,13 @@
     {"get_handle_inheritable", (PyCFunction)os_get_handle_inheritable, METH_O, os_get_handle_inheritable__doc__},
 
 static int
-os_get_handle_inheritable_impl(PyObject *module, Py_intptr_t handle);
+os_get_handle_inheritable_impl(PyObject *module, intptr_t handle);
 
 static PyObject *
 os_get_handle_inheritable(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    Py_intptr_t handle;
+    intptr_t handle;
     int _return_value;
 
     if (!PyArg_Parse(arg, "" _Py_PARSE_INTPTR ":get_handle_inheritable", &handle)) {
@@ -5515,14 +5515,14 @@
     {"set_handle_inheritable", (PyCFunction)os_set_handle_inheritable, METH_VARARGS, os_set_handle_inheritable__doc__},
 
 static PyObject *
-os_set_handle_inheritable_impl(PyObject *module, Py_intptr_t handle,
+os_set_handle_inheritable_impl(PyObject *module, intptr_t handle,
                                int inheritable);
 
 static PyObject *
 os_set_handle_inheritable(PyObject *module, PyObject *args)
 {
     PyObject *return_value = NULL;
-    Py_intptr_t handle;
+    intptr_t handle;
     int inheritable;
 
     if (!PyArg_ParseTuple(args, "" _Py_PARSE_INTPTR "p:set_handle_inheritable",
@@ -6042,4 +6042,4 @@
 #ifndef OS_SET_HANDLE_INHERITABLE_METHODDEF
     #define OS_SET_HANDLE_INHERITABLE_METHODDEF
 #endif /* !defined(OS_SET_HANDLE_INHERITABLE_METHODDEF) */
-/*[clinic end generated code: output=2b85bb3703a6488a input=a9049054013a1b77]*/
+/*[clinic end generated code: output=677ce794fb126161 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -2520,7 +2520,7 @@
     impl_by_reference = True;
 
 [python start generated code]*/
-/*[python end generated code: output=da39a3ee5e6b4b0d input=affe68316f160401]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=418fce0e01144461]*/
 
 /*[clinic input]
 
@@ -7092,7 +7092,7 @@
 
 static PyObject *
 os_waitpid_impl(PyObject *module, intptr_t pid, int options)
-/*[clinic end generated code: output=15f1ce005a346b09 input=444c8f51cca5b862]*/
+/*[clinic end generated code: output=be836b221271d538 input=40f2440c515410f8]*/
 {
     int status;
     intptr_t res;
@@ -11383,7 +11383,7 @@
 
 static int
 os_get_handle_inheritable_impl(PyObject *module, intptr_t handle)
-/*[clinic end generated code: output=9e5389b0aa0916ce input=5f7759443aae3dc5]*/
+/*[clinic end generated code: output=36be5afca6ea84d8 input=cfe99f9c05c70ad1]*/
 {
     DWORD flags;
 
@@ -11408,7 +11408,7 @@
 static PyObject *
 os_set_handle_inheritable_impl(PyObject *module, intptr_t handle,
                                int inheritable)
-/*[clinic end generated code: output=b1e67bfa3213d745 input=e64b2b2730469def]*/
+/*[clinic end generated code: output=021d74fe6c96baa3 input=7a7641390d8364fc]*/
 {
     DWORD flags = inheritable ? HANDLE_FLAG_INHERIT : 0;
     if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) {
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -3842,6 +3842,7 @@
 int
 PyUnicode_FSConverter(PyObject* arg, void* addr)
 {
+    PyObject *path = NULL;
     PyObject *output = NULL;
     Py_ssize_t size;
     void *data;
@@ -3850,22 +3851,22 @@
         *(PyObject**)addr = NULL;
         return 1;
     }
-    if (PyBytes_Check(arg)) {
-        output = arg;
-        Py_INCREF(output);
-    }
-    else if (PyUnicode_Check(arg)) {
-        output = PyUnicode_EncodeFSDefault(arg);
-        if (!output)
+    path = PyOS_FSPath(arg);
+    if (path == NULL) {
+        return 0;
+    }
+    if (PyBytes_Check(path)) {
+        output = path;
+    }
+    else {  // PyOS_FSPath() guarantees its returned value is bytes or str.
+        output = PyUnicode_EncodeFSDefault(path);
+        Py_DECREF(path);
+        if (!output) {
             return 0;
+        }
         assert(PyBytes_Check(output));
     }
-    else {
-        PyErr_Format(PyExc_TypeError,
-                     "must be str or bytes, not %.100s",
-                     Py_TYPE(arg)->tp_name);
-        return 0;
-    }
+
     size = PyBytes_GET_SIZE(output);
     data = PyBytes_AS_STRING(output);
     if ((size_t)size != strlen(data)) {

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list