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

This commit is contained in:
Ethan Furman 2016-06-04 12:06:26 -07:00
parent c55014f371
commit 410ef8e230
6 changed files with 125 additions and 18 deletions

View File

@ -116,6 +116,7 @@
#include "pylifecycle.h"
#include "ceval.h"
#include "sysmodule.h"
#include "osmodule.h"
#include "intrcheck.h"
#include "import.h"

15
Include/osmodule.h Normal file
View File

@ -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 */

View File

@ -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__)

View 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():

View 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]*/

View 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 */
};