Commit 410ef8e2 authored by Ethan Furman's avatar Ethan Furman

issue27186: add C version of os.fspath(); patch by Jelle Zijlstra

parent c55014f3
......@@ -116,6 +116,7 @@
#include "pylifecycle.h"
#include "ceval.h"
#include "sysmodule.h"
#include "osmodule.h"
#include "intrcheck.h"
#include "import.h"
......
/* 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 */
......@@ -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__)
......@@ -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():
......
......@@ -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]*/
......@@ -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 */
};
......
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