diff options
-rw-r--r-- | Doc/c-api/unicode.rst | 12 | ||||
-rw-r--r-- | Doc/whatsnew/3.6.rst | 18 | ||||
-rw-r--r-- | Lib/test/test_compile.py | 10 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Objects/unicodeobject.c | 51 |
5 files changed, 68 insertions, 26 deletions
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 019453f..55ef575 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -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) diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 69f8f39..0517ef8 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -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 diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 9638e69..409ec86 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -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 @@ -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 ----- diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index aa0402a..2279442 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -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) { |