]> granicus.if.org Git - python/commitdiff
Issue #27182: Add support for path-like objects to PyUnicode_FSDecoder().
authorBrett Cannon <brett@python.org>
Wed, 7 Sep 2016 02:36:01 +0000 (19:36 -0700)
committerBrett Cannon <brett@python.org>
Wed, 7 Sep 2016 02:36:01 +0000 (19:36 -0700)
Doc/c-api/unicode.rst
Doc/whatsnew/3.6.rst
Lib/test/test_compile.py
Misc/NEWS
Objects/unicodeobject.c

index 019453fac892e7d632bd8663e392b6e64b7722e4..55ef5750f51d2b08a9bb2ffcf91570e6baa9da6a 100644 (file)
@@ -826,13 +826,17 @@ used, passing :c:func:`PyUnicode_FSDecoder` as the conversion function:
 
 .. c:function:: int PyUnicode_FSDecoder(PyObject* obj, void* result)
 
-   ParseTuple converter: decode :class:`bytes` objects to :class:`str` using
-   :c:func:`PyUnicode_DecodeFSDefaultAndSize`; :class:`str` objects are output
-   as-is. *result* must be a :c:type:`PyUnicodeObject*` which must be released
-   when it is no longer used.
+   ParseTuple converter: decode :class:`bytes` objects -- obtained either
+   directly or indirectly through the :class:`os.PathLike` interface -- to
+   :class:`str` using :c:func:`PyUnicode_DecodeFSDefaultAndSize`; :class:`str`
+   objects are output as-is. *result* must be a :c:type:`PyUnicodeObject*` which
+   must be released when it is no longer used.
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.6
+      Accepts a :term:`path-like object`.
+
 
 .. c:function:: PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size)
 
index 69f8f393340ec90026ca1e955b01ff74977a5271..0517ef8376fa4dca88e9f236a68e92e0c688f333 100644 (file)
@@ -160,14 +160,18 @@ object.
 
 The built-in :func:`open` function has been updated to accept
 :class:`os.PathLike` objects as have all relevant functions in the
-:mod:`os` and :mod:`os.path` modules. The :class:`os.DirEntry` class
+:mod:`os` and :mod:`os.path` modules. :c:func:`PyUnicode_FSConverter`
+and :c:func:`PyUnicode_FSConverter` have been changed to accept
+path-like objects. The :class:`os.DirEntry` class
 and relevant classes in :mod:`pathlib` have also been updated to
-implement :class:`os.PathLike`. The hope is that updating the
-fundamental functions for operating on file system paths will lead
-to third-party code to implicitly support all
-:term:`path-like objects <path-like object>` without any code changes
-or at least very minimal ones (e.g. calling :func:`os.fspath` at the
-beginning of code before operating on a path-like object).
+implement :class:`os.PathLike`.
+
+The hope in is that updating the fundamental functions for operating
+on file system paths will lead to third-party code to implicitly
+support all :term:`path-like objects <path-like object>` without any
+code changes or at least very minimal ones (e.g. calling
+:func:`os.fspath` at the beginning of code before operating on a
+path-like object).
 
 Here are some examples of how the new interface allows for
 :class:`pathlib.Path` to be used more easily and transparently with
index 9638e6975a222e18961cd7b037ec8cb65ce6b7e5..409ec86c75cd34d1be07230c6910b6f3066c365e 100644 (file)
@@ -664,6 +664,16 @@ if 1:
         self.assertTrue(f1(0))
         self.assertTrue(f2(0.0))
 
+    def test_path_like_objects(self):
+        # An implicit test for PyUnicode_FSDecoder().
+        class PathLike:
+            def __init__(self, path):
+                self._path = path
+            def __fspath__(self):
+                return self._path
+
+        compile("42", PathLike("test_compile_pathlike"), "single")
+
 
 class TestStackSize(unittest.TestCase):
     # These tests check that the computed stack size for a code object
index fbf7b2b976470e7809c5161410b05ec2cc75e4b5..f5e8aaa50f9ad7251760e6bd64f3ba335a38b724 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -202,7 +202,8 @@ Library
 C API
 -----
 
-- Issue #26027: Add support for path-like objects in PyUnicode_FSConverter().
+- Issue #26027: Add support for path-like objects in PyUnicode_FSConverter() &
+  PyUnicode_FSDecoder().
 
 Tests
 -----
index aa0402adc0b1553ba17949464df97580407b3218..2279442cc858fd0eff45daa87c1bbe5af1c97976 100644 (file)
@@ -3882,37 +3882,60 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
 int
 PyUnicode_FSDecoder(PyObject* arg, void* addr)
 {
+    int is_buffer = 0;
+    PyObject *path = NULL;
     PyObject *output = NULL;
     if (arg == NULL) {
         Py_DECREF(*(PyObject**)addr);
         return 1;
     }
-    if (PyUnicode_Check(arg)) {
-        if (PyUnicode_READY(arg) == -1)
+
+    is_buffer = PyObject_CheckBuffer(arg);
+    if (!is_buffer) {
+        path = PyOS_FSPath(arg);
+        if (path == NULL) {
+            return 0;
+        }
+    }
+    else {
+        path = arg;
+        Py_INCREF(arg);
+    }
+
+    if (PyUnicode_Check(path)) {
+        if (PyUnicode_READY(path) == -1) {
+            Py_DECREF(path);
             return 0;
-        output = arg;
-        Py_INCREF(output);
+        }
+        output = path;
     }
-    else if (PyBytes_Check(arg) || PyObject_CheckBuffer(arg)) {
-        if (!PyBytes_Check(arg) &&
+    else if (PyBytes_Check(path) || is_buffer) {
+        PyObject *path_bytes = NULL;
+
+        if (!PyBytes_Check(path) &&
             PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
-            "path should be string or bytes, not %.200s",
+            "path should be string, bytes, or os.PathLike, not %.200s",
             Py_TYPE(arg)->tp_name)) {
+                Py_DECREF(path);
             return 0;
         }
-        arg = PyBytes_FromObject(arg);
-        if (!arg)
+        path_bytes = PyBytes_FromObject(path);
+        Py_DECREF(path);
+        if (!path_bytes) {
             return 0;
-        output = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AS_STRING(arg),
-                                                  PyBytes_GET_SIZE(arg));
-        Py_DECREF(arg);
-        if (!output)
+        }
+        output = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AS_STRING(path_bytes),
+                                                  PyBytes_GET_SIZE(path_bytes));
+        Py_DECREF(path_bytes);
+        if (!output) {
             return 0;
+        }
     }
     else {
         PyErr_Format(PyExc_TypeError,
-                     "path should be string or bytes, not %.200s",
+                     "path should be string, bytes, or os.PathLike, not %.200s",
                      Py_TYPE(arg)->tp_name);
+        Py_DECREF(path);
         return 0;
     }
     if (PyUnicode_READY(output) == -1) {