]> granicus.if.org Git - python/commitdiff
issue27186: add C version of os.fspath(); patch by Jelle Zijlstra
authorEthan Furman <ethan@stoneleaf.us>
Sat, 4 Jun 2016 19:06:26 +0000 (12:06 -0700)
committerEthan Furman <ethan@stoneleaf.us>
Sat, 4 Jun 2016 19:06:26 +0000 (12:06 -0700)
Include/Python.h
Include/osmodule.h [new file with mode: 0644]
Lib/os.py
Lib/test/test_os.py
Modules/clinic/posixmodule.c.h
Modules/posixmodule.c

index 858dbd1a66a4ea381096324bbddcfa06240d8438..4c7c9a48c81c2a3345a7168224e3979ddf3ac918 100644 (file)
 #include "pylifecycle.h"
 #include "ceval.h"
 #include "sysmodule.h"
+#include "osmodule.h"
 #include "intrcheck.h"
 #include "import.h"
 
diff --git a/Include/osmodule.h b/Include/osmodule.h
new file mode 100644 (file)
index 0000000..7146757
--- /dev/null
@@ -0,0 +1,15 @@
+
+/* os module interface */
+
+#ifndef Py_OSMODULE_H
+#define Py_OSMODULE_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+PyAPI_FUNC(PyObject *) PyOS_FSPath(PyObject *path);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_OSMODULE_H */
index 1318de6b5add541ba39d760340b4cff531820587..0131ed8195edcc7bdf1701a49db13046233356f5 100644 (file)
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -1104,23 +1104,24 @@ def fdopen(fd, *args, **kwargs):
     import io
     return io.open(fd, *args, **kwargs)
 
-# Supply os.fspath()
-def fspath(path):
-    """Return the string representation of the path.
+# Supply os.fspath() if not defined in C
+if not _exists('fspath'):
+    def fspath(path):
+        """Return the string representation of the path.
 
-    If str or bytes is passed in, it is returned unchanged.
-    """
-    if isinstance(path, (str, bytes)):
-        return path
+        If str or bytes is passed in, it is returned unchanged.
+        """
+        if isinstance(path, (str, bytes)):
+            return path
 
-    # Work from the object's type to match method resolution of other magic
-    # methods.
-    path_type = type(path)
-    try:
-        return path_type.__fspath__(path)
-    except AttributeError:
-        if hasattr(path_type, '__fspath__'):
-            raise
+        # Work from the object's type to match method resolution of other magic
+        # methods.
+        path_type = type(path)
+        try:
+            return path_type.__fspath__(path)
+        except AttributeError:
+            if hasattr(path_type, '__fspath__'):
+                raise
 
-        raise TypeError("expected str, bytes or os.PathLike object, not "
-                        + path_type.__name__)
+            raise TypeError("expected str, bytes or os.PathLike object, not "
+                            + path_type.__name__)
index 84ef150f8275961ffd39205872875c8b7a2c6145..bf06438db224f4da5fb06aa60a73a0dec44eb337 100644 (file)
@@ -3121,6 +3121,13 @@ class TestPEP519(unittest.TestCase):
             self.assertEqual(b"path/like/object", os.fsencode(pathlike))
             self.assertEqual("path/like/object", os.fsdecode(pathlike))
 
+    def test_fspathlike(self):
+        class PathLike(object):
+            def __fspath__(self):
+                return '#feelthegil'
+
+        self.assertEqual('#feelthegil', os.fspath(PathLike()))
+
     def test_garbage_in_exception_out(self):
         vapor = type('blah', (), {})
         for o in int, type, os, vapor():
index a48de6ac882b2b28705375d760e4088fa6f65e69..2758d48cf03cf8051abf2c226c193c7bef8fb0f0 100644 (file)
@@ -5321,6 +5321,38 @@ exit:
 
 #endif /* defined(MS_WINDOWS) */
 
+PyDoc_STRVAR(os_fspath__doc__,
+"fspath($module, /, path)\n"
+"--\n"
+"\n"
+"Return the file system path representation of the object.\n"
+"\n"
+"If the object is str or bytes, then allow it to pass through with\n"
+"an incremented refcount. If the object defines __fspath__(), then\n"
+"return the result of that method. All other types raise a TypeError.");
+
+#define OS_FSPATH_METHODDEF    \
+    {"fspath", (PyCFunction)os_fspath, METH_VARARGS|METH_KEYWORDS, os_fspath__doc__},
+
+static PyObject *
+os_fspath_impl(PyModuleDef *module, PyObject *path);
+
+static PyObject *
+os_fspath(PyModuleDef *module, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    static char *_keywords[] = {"path", NULL};
+    PyObject *path;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:fspath", _keywords,
+        &path))
+        goto exit;
+    return_value = os_fspath_impl(module, path);
+
+exit:
+    return return_value;
+}
+
 #ifndef OS_TTYNAME_METHODDEF
     #define OS_TTYNAME_METHODDEF
 #endif /* !defined(OS_TTYNAME_METHODDEF) */
@@ -5792,4 +5824,4 @@ exit:
 #ifndef OS_SET_HANDLE_INHERITABLE_METHODDEF
     #define OS_SET_HANDLE_INHERITABLE_METHODDEF
 #endif /* !defined(OS_SET_HANDLE_INHERITABLE_METHODDEF) */
-/*[clinic end generated code: output=a5c9bef9ad11a20b input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e64e246b8270abda input=a9049054013a1b77]*/
index ded6d716eb2b76c3cc44bc784c2a8b890db715d2..c55226576c9305693624597fff8a1ba775ce6733 100644 (file)
@@ -12284,6 +12284,56 @@ error:
     return NULL;
 }
 
+/*
+    Return the file system path representation of the object.
+
+    If the object is str or bytes, then allow it to pass through with
+    an incremented refcount. If the object defines __fspath__(), then
+    return the result of that method. All other types raise a TypeError.
+*/
+PyObject *
+PyOS_FSPath(PyObject *path)
+{
+    _Py_IDENTIFIER(__fspath__);
+    PyObject *func = NULL;
+    PyObject *path_repr = NULL;
+
+    if (PyUnicode_Check(path) || PyBytes_Check(path)) {
+        Py_INCREF(path);
+        return path;
+    }
+
+    func = _PyObject_LookupSpecial(path, &PyId___fspath__);
+    if (NULL == func) {
+        return PyErr_Format(PyExc_TypeError,
+                            "expected str, bytes or os.PathLike object, "
+                            "not %S",
+                            path->ob_type);
+    }
+
+    path_repr = PyObject_CallFunctionObjArgs(func, NULL);
+    Py_DECREF(func);
+    return path_repr;
+}
+
+/*[clinic input]
+os.fspath
+
+    path: object
+
+Return the file system path representation of the object.
+
+If the object is str or bytes, then allow it to pass through with
+an incremented refcount. If the object defines __fspath__(), then
+return the result of that method. All other types raise a TypeError.
+[clinic start generated code]*/
+
+static PyObject *
+os_fspath_impl(PyModuleDef *module, PyObject *path)
+/*[clinic end generated code: output=51ef0c2772c1932a input=652c7c37e4be1c13]*/
+{
+    return PyOS_FSPath(path);
+}
 
 #include "clinic/posixmodule.c.h"
 
@@ -12484,6 +12534,7 @@ static PyMethodDef posix_methods[] = {
     {"scandir",         (PyCFunction)posix_scandir,
                         METH_VARARGS | METH_KEYWORDS,
                         posix_scandir__doc__},
+    OS_FSPATH_METHODDEF
     {NULL,              NULL}            /* Sentinel */
 };