cpython: b64f83d6ff24 (original) (raw)
Mercurial > cpython
changeset 102920:b64f83d6ff24
Issue #26027, #27524: Add PEP 519/__fspath__() support to os and os.path. Thanks to Jelle Zijlstra for the initial patch against posixmodule.c. [#26027]
Brett Cannon brett@python.org | |
---|---|
date | Fri, 26 Aug 2016 14:44:48 -0700 |
parents | 685f32972c11 |
children | 95361959d451 |
files | Lib/genericpath.py Lib/ntpath.py Lib/os.py Lib/posixpath.py Lib/test/test_genericpath.py Lib/test/test_ntpath.py Lib/test/test_os.py Lib/test/test_posix.py Lib/test/test_posixpath.py Misc/NEWS Modules/posixmodule.c |
diffstat | 11 files changed, 428 insertions(+), 56 deletions(-)[+] [-] Lib/genericpath.py 6 Lib/ntpath.py 18 Lib/os.py 4 Lib/posixpath.py 19 Lib/test/test_genericpath.py 64 Lib/test/test_ntpath.py 83 Lib/test/test_os.py 83 Lib/test/test_posix.py 10 Lib/test/test_posixpath.py 80 Misc/NEWS 5 Modules/posixmodule.c 112 |
line wrap: on
line diff
--- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -69,6 +69,12 @@ def getctime(filename): def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return ''
Some people pass in a list of pathname parts to operate in an OS-agnostic
fashion; don't try to translate in that case as that's an abuse of the
API and they are already doing what they need to be OS-agnostic and so
they most likely won't be using an os.PathLike object in the sublists.
- if not isinstance(m[0], (list, tuple)):
s1 = min(m) s2 = max(m) for i, c in enumerate(s1):m = tuple(map(os.fspath, m))[](#l1.12)
--- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -46,6 +46,7 @@ def normcase(s): """Normalize case of pathname. Makes all characters lowercase and all slashes into backslashes."""
@@ -66,12 +67,14 @@ def normcase(s): def isabs(s): """Test whether a path is absolute"""
Join two (or more) paths.
@@ -84,7 +87,7 @@ def join(path, *paths): if not paths: path[:0] + sep #23780: Ensure compatible data type even if p is null. result_drive, result_path = splitdrive(path)
for p in paths:[](#l2.30)
for p in map(os.fspath, paths):[](#l2.31) p_drive, p_path = splitdrive(p)[](#l2.32) if p_path and p_path[0] in seps:[](#l2.33) # Second path is absolute[](#l2.34)
@@ -136,6 +139,7 @@ def splitdrive(p): Paths cannot contain both a drive letter and a UNC path. """
@@ -199,7 +203,7 @@ def split(p): Return tuple (head, tail) where tail is everything after the final slash. Either part may be empty.""" -
@@ -218,6 +222,7 @@ def split(p):
It is always true that root + ext == p.
- p = os.fspath(p) if isinstance(p, bytes): return genericpath._splitext(p, b'\', b'/', b'.') else: @@ -278,6 +283,7 @@ except ImportError: def ismount(path): """Test whether a path is a mount point (a drive root, the root of a share, or a mounted volume)"""
- path = os.fspath(path) seps = _get_bothseps(path) path = abspath(path) root, rest = splitdrive(path) @@ -305,6 +311,7 @@ def expanduser(path): """Expand ~ and ~user constructs. If user or $HOME is unknown, do nothing."""
- path = os.fspath(path) if isinstance(path, bytes): tilde = b'~' else: @@ -354,6 +361,7 @@ def expandvars(path): """Expand shell variables of the forms var,var, var,{var} and %var%. Unknown variables are left unchanged."""
- path = os.fspath(path) if isinstance(path, bytes): if b'$' not in path and b'%' not in path: return path
@@ -464,6 +472,7 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc."""
@@ -518,6 +527,7 @@ try: except ImportError: # not running on Windows - mock up something sensible def abspath(path): """Return the absolute version of a path."""
path = os.fspath(path)[](#l2.96) if not isabs(path):[](#l2.97) if isinstance(path, bytes):[](#l2.98) cwd = os.getcwdb()[](#l2.99)
@@ -531,6 +541,7 @@ else: # use native Windows method on Wi """Return the absolute version of a path.""" if path: # Empty path must return current working directory.
path = os.fspath(path)[](#l2.104) try:[](#l2.105) path = _getfullpathname(path)[](#l2.106) except OSError:[](#l2.107)
@@ -549,6 +560,7 @@ supports_unicode_filenames = (hasattr(sy def relpath(path, start=None): """Return a relative version of a path"""
@@ -564,6 +576,7 @@ def relpath(path, start=None): if not path: raise ValueError("no path specified")
- start = os.fspath(start) try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path))
@@ -607,6 +620,7 @@ def commonpath(paths): if not paths: raise ValueError('commonpath() arg is an empty sequence')
--- a/Lib/os.py +++ b/Lib/os.py @@ -353,7 +353,7 @@ def walk(top, topdown=True, onerror=None dirs.remove('CVS') # don't visit CVS directories """ -
- top = fspath(top) dirs = [] nondirs = [] walk_dirs = [] @@ -536,6 +536,8 @@ if {open, stat} <= supports_dir_fd and { if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories """
if not isinstance(top, int) or not hasattr(top, '__index__'):[](#l3.16)
top = fspath(top)[](#l3.17) # Note: To guard against symlink races, we use the standard[](#l3.18) # lstat()/open()/fstat() trick.[](#l3.19) orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)[](#l3.20)
--- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -49,6 +49,7 @@ def _get_sep(path): def normcase(s): """Normalize case of pathname. Has no effect under Posix"""
- s = os.fspath(s) if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.class.name))
@@ -60,6 +61,7 @@ def normcase(s): def isabs(s): """Test whether a path is absolute"""
- s = os.fspath(s) sep = _get_sep(s) return s.startswith(sep) @@ -73,12 +75,13 @@ def join(a, *p): If any component is an absolute path, all previous path components will be discarded. An empty last part will result in a path that ends with a separator."""
- a = os.fspath(a) sep = _get_sep(a) path = a try: if not p: path[:0] + sep #23780: Ensure compatible data type even if p is null.
for b in p:[](#l4.29)
for b in map(os.fspath, p):[](#l4.30) if b.startswith(sep):[](#l4.31) path = b[](#l4.32) elif not path or path.endswith(sep):[](#l4.33)
@@ -99,6 +102,7 @@ def join(a, *p): def split(p): """Split a pathname. Returns tuple "(head, tail)" where "tail" is everything after the final slash. Either part may be empty."""
- p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] @@ -113,6 +117,7 @@ def split(p):
It is always true that root + ext == p.
@@ -128,6 +133,7 @@ splitext.doc = genericpath._splitext def splitdrive(p): """Split a pathname into drive and path. On Posix, drive is always empty."""
- p = os.fspath(p) return p[:0], p @@ -135,6 +141,7 @@ def splitdrive(p): def basename(p): """Returns the final component of a pathname"""
- p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 return p[i:] @@ -144,6 +151,7 @@ def basename(p): def dirname(p): """Returns the directory component of a pathname"""
- p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head = p[:i] @@ -222,6 +230,7 @@ def ismount(path): def expanduser(path): """Expand ~ and ~user constructions. If user or $HOME is unknown, do nothing."""
- path = os.fspath(path) if isinstance(path, bytes): tilde = b'~' else: @@ -267,6 +276,7 @@ def expanduser(path): def expandvars(path): """Expand shell variables of form varandvar and varand{var}. Unknown variables are left unchanged."""
- path = os.fspath(path) global _varprog, _varprogb if isinstance(path, bytes): if b'$' not in path:
@@ -318,6 +328,7 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc."""
@@ -355,6 +366,7 @@ def normpath(path): def abspath(path): """Return an absolute path."""
@@ -370,6 +382,7 @@ def abspath(path): def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path."""
- filename = os.fspath(filename) path, ok = _joinrealpath(filename[:0], filename, {}) return abspath(path) @@ -434,6 +447,7 @@ def relpath(path, start=None): if not path: raise ValueError("no path specified")
@@ -445,6 +459,8 @@ def relpath(path, start=None): if start is None: start = curdir
try: start_list = [x for x in abspath(start).split(sep) if x] @@ -472,6 +488,7 @@ def commonpath(paths): if not paths: raise ValueError('commonpath() arg is an empty sequence')
--- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -450,16 +450,15 @@ class CommonTest(GenericTest): with self.assertRaisesRegex(TypeError, errmsg): self.pathmodule.join('str', b'bytes') # regression, see #15377
errmsg = r'join\(\) argument must be str or bytes, not %r'[](#l5.7)
with self.assertRaisesRegex(TypeError, errmsg % 'int'):[](#l5.8)
with self.assertRaisesRegex(TypeError, 'int'):[](#l5.9) self.pathmodule.join(42, 'str')[](#l5.10)
with self.assertRaisesRegex(TypeError, errmsg % 'int'):[](#l5.11)
with self.assertRaisesRegex(TypeError, 'int'):[](#l5.12) self.pathmodule.join('str', 42)[](#l5.13)
with self.assertRaisesRegex(TypeError, errmsg % 'int'):[](#l5.14)
with self.assertRaisesRegex(TypeError, 'int'):[](#l5.15) self.pathmodule.join(42)[](#l5.16)
with self.assertRaisesRegex(TypeError, errmsg % 'list'):[](#l5.17)
with self.assertRaisesRegex(TypeError, 'list'):[](#l5.18) self.pathmodule.join([])[](#l5.19)
with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'):[](#l5.20)
with self.assertRaisesRegex(TypeError, 'bytearray'):[](#l5.21) self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar'))[](#l5.22)
def test_relpath_errors(self): @@ -471,14 +470,59 @@ class CommonTest(GenericTest): self.pathmodule.relpath(b'bytes', 'str') with self.assertRaisesRegex(TypeError, errmsg): self.pathmodule.relpath('str', b'bytes')
errmsg = r'relpath\(\) argument must be str or bytes, not %r'[](#l5.29)
with self.assertRaisesRegex(TypeError, errmsg % 'int'):[](#l5.30)
with self.assertRaisesRegex(TypeError, 'int'):[](#l5.31) self.pathmodule.relpath(42, 'str')[](#l5.32)
with self.assertRaisesRegex(TypeError, errmsg % 'int'):[](#l5.33)
with self.assertRaisesRegex(TypeError, 'int'):[](#l5.34) self.pathmodule.relpath('str', 42)[](#l5.35)
with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'):[](#l5.36)
with self.assertRaisesRegex(TypeError, 'bytearray'):[](#l5.37) self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar'))[](#l5.38)
+class PathLikeTests(unittest.TestCase): +
- class PathLike:
def __init__(self, path=''):[](#l5.44)
self.path = path[](#l5.45)
def __fspath__(self):[](#l5.46)
if isinstance(self.path, BaseException):[](#l5.47)
raise self.path[](#l5.48)
else:[](#l5.49)
return self.path[](#l5.50)
- def setUp(self):
self.file_name = support.TESTFN.lower()[](#l5.53)
self.file_path = self.PathLike(support.TESTFN)[](#l5.54)
self.addCleanup(support.unlink, self.file_name)[](#l5.55)
create_file(self.file_name, b"test_genericpath.PathLikeTests")[](#l5.56)
- def assertPathEqual(self, func):
self.assertEqual(func(self.file_path), func(self.file_name))[](#l5.59)
- def test_path_commonprefix(self):
self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]),[](#l5.71)
self.file_name)[](#l5.72)
- def test_path_samefile(self):
self.assertTrue(os.path.samefile(self.file_path, self.file_name))[](#l5.84)
+ + if name=="main": unittest.main()
--- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -452,5 +452,88 @@ class NtCommonTest(test_genericpath.Comm attributes = ['relpath', 'splitunc'] +class PathLikeTests(unittest.TestCase): +
- class PathLike:
def __init__(self, path=''):[](#l6.12)
self.path = path[](#l6.13)
def __fspath__(self):[](#l6.14)
if isinstance(self.path, BaseException):[](#l6.15)
raise self.path[](#l6.16)
else:[](#l6.17)
return self.path[](#l6.18)
- def setUp(self):
self.file_name = support.TESTFN.lower()[](#l6.21)
self.file_path = self.PathLike(support.TESTFN)[](#l6.22)
self.addCleanup(support.unlink, self.file_name)[](#l6.23)
with open(self.file_name, 'xb', 0) as file:[](#l6.24)
file.write(b"test_ntpath.PathLikeTests")[](#l6.25)
- def assertPathEqual(self, func):
self.assertEqual(func(self.file_path), func(self.file_name))[](#l6.28)
- def test_path_join(self):
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),[](#l6.37)
self.path.join('a', 'b', 'c'))[](#l6.38)
- def test_path_commonpath(self):
common_path = self.path.commonpath([self.file_path, self.file_name])[](#l6.83)
self.assertEqual(common_path, self.file_name)[](#l6.84)
+ + if name == "main": unittest.main()
--- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -874,10 +874,12 @@ class WalkTests(unittest.TestCase): self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) self.assertEqual(all[3 - 2 * flipped], self.sub2_tree)
- def test_walk_prune(self, walk_path=None):
if walk_path is None:[](#l7.9)
walk_path = self.walk_path[](#l7.10) # Prune the search.[](#l7.11) all = [][](#l7.12)
for root, dirs, files in self.walk(self.walk_path):[](#l7.13)
for root, dirs, files in self.walk(walk_path):[](#l7.14) all.append((root, dirs, files))[](#l7.15) # Don't descend into SUB1.[](#l7.16) if 'SUB1' in dirs:[](#l7.17)
@@ -886,11 +888,22 @@ class WalkTests(unittest.TestCase): self.assertEqual(len(all), 2) self.assertEqual(all[0],
(self.walk_path, ["SUB2"], ["tmp1"]))[](#l7.22)
(str(walk_path), ["SUB2"], ["tmp1"]))[](#l7.23)
all[1][-1].sort() self.assertEqual(all[1], self.sub2_tree)
- def test_file_like_path(self):
class FileLike:[](#l7.29)
def __init__(self, path):[](#l7.30)
self._path = path[](#l7.31)
def __str__(self):[](#l7.32)
return str(self._path)[](#l7.33)
def __fspath__(self):[](#l7.34)
return self._path[](#l7.35)
self.test_walk_prune(FileLike(self.walk_path))[](#l7.37)
+ def test_walk_bottom_up(self): # Walk bottom-up. all = list(self.walk(self.walk_path, topdown=False)) @@ -2807,6 +2820,70 @@ class FDInheritanceTests(unittest.TestCa self.assertEqual(os.get_inheritable(slave_fd), False) +class PathTConverterTests(unittest.TestCase):
tuples of (function name, allows fd arguments, additional arguments to
function, cleanup function)
- functions = [
('stat', True, (), None),[](#l7.50)
('lstat', False, (), None),[](#l7.51)
('access', True, (os.F_OK,), None),[](#l7.52)
('chflags', False, (0,), None),[](#l7.53)
('lchflags', False, (0,), None),[](#l7.54)
('open', False, (0,), getattr(os, 'close', None)),[](#l7.55)
- ]
- def test_path_t_converter(self):
class PathLike:[](#l7.59)
def __init__(self, path):[](#l7.60)
self.path = path[](#l7.61)
def __fspath__(self):[](#l7.63)
return self.path[](#l7.64)
str_filename = support.TESTFN[](#l7.66)
bytes_filename = support.TESTFN.encode('ascii')[](#l7.67)
bytearray_filename = bytearray(bytes_filename)[](#l7.68)
fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT)[](#l7.69)
self.addCleanup(os.close, fd)[](#l7.70)
self.addCleanup(support.unlink, support.TESTFN)[](#l7.71)
int_fspath = PathLike(fd)[](#l7.73)
str_fspath = PathLike(str_filename)[](#l7.74)
bytes_fspath = PathLike(bytes_filename)[](#l7.75)
bytearray_fspath = PathLike(bytearray_filename)[](#l7.76)
for name, allow_fd, extra_args, cleanup_fn in self.functions:[](#l7.78)
with self.subTest(name=name):[](#l7.79)
try:[](#l7.80)
fn = getattr(os, name)[](#l7.81)
except AttributeError:[](#l7.82)
continue[](#l7.83)
for path in (str_filename, bytes_filename, bytearray_filename,[](#l7.85)
str_fspath, bytes_fspath):[](#l7.86)
with self.subTest(name=name, path=path):[](#l7.87)
result = fn(path, *extra_args)[](#l7.88)
if cleanup_fn is not None:[](#l7.89)
cleanup_fn(result)[](#l7.90)
with self.assertRaisesRegex([](#l7.92)
TypeError, 'should be string, bytes'):[](#l7.93)
fn(int_fspath, *extra_args)[](#l7.94)
with self.assertRaisesRegex([](#l7.95)
TypeError, 'should be string, bytes'):[](#l7.96)
fn(bytearray_fspath, *extra_args)[](#l7.97)
if allow_fd:[](#l7.99)
result = fn(fd, *extra_args) # should not fail[](#l7.100)
if cleanup_fn is not None:[](#l7.101)
cleanup_fn(result)[](#l7.102)
else:[](#l7.103)
with self.assertRaisesRegex([](#l7.104)
TypeError,[](#l7.105)
'os.PathLike'):[](#l7.106)
fn(fd, *extra_args)[](#l7.107)
+ + @unittest.skipUnless(hasattr(os, 'get_blocking'), 'needs os.get_blocking() and os.set_blocking()') class BlockingTests(unittest.TestCase):
--- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -397,7 +397,7 @@ class PosixTester(unittest.TestCase): self.assertTrue(posix.stat(fp.fileno())) self.assertRaisesRegex(TypeError,
'should be string, bytes or integer, not',[](#l8.7)
'should be string, bytes, os.PathLike or integer, not',[](#l8.8) posix.stat, float(fp.fileno()))[](#l8.9) finally:[](#l8.10) fp.close()[](#l8.11)
@@ -409,16 +409,16 @@ class PosixTester(unittest.TestCase): self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) self.assertWarnsRegex(DeprecationWarning,
'should be string, bytes or integer, not',[](#l8.16)
'should be string, bytes, os.PathLike or integer, not',[](#l8.17) posix.stat, bytearray(os.fsencode(support.TESTFN)))[](#l8.18) self.assertRaisesRegex(TypeError,[](#l8.19)
'should be string, bytes or integer, not',[](#l8.20)
'should be string, bytes, os.PathLike or integer, not',[](#l8.21) posix.stat, None)[](#l8.22) self.assertRaisesRegex(TypeError,[](#l8.23)
'should be string, bytes or integer, not',[](#l8.24)
'should be string, bytes, os.PathLike or integer, not',[](#l8.25) posix.stat, list(support.TESTFN))[](#l8.26) self.assertRaisesRegex(TypeError,[](#l8.27)
'should be string, bytes or integer, not',[](#l8.28)
'should be string, bytes, os.PathLike or integer, not',[](#l8.29) posix.stat, list(os.fsencode(support.TESTFN)))[](#l8.30)
@unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()")
--- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -596,5 +596,85 @@ class PosixCommonTest(test_genericpath.C attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat'] +class PathLikeTests(unittest.TestCase): +
- class PathLike:
def __init__(self, path=''):[](#l9.12)
self.path = path[](#l9.13)
def __fspath__(self):[](#l9.14)
if isinstance(self.path, BaseException):[](#l9.15)
raise self.path[](#l9.16)
else:[](#l9.17)
return self.path[](#l9.18)
- def setUp(self):
self.file_name = support.TESTFN.lower()[](#l9.21)
self.file_path = self.PathLike(support.TESTFN)[](#l9.22)
self.addCleanup(support.unlink, self.file_name)[](#l9.23)
with open(self.file_name, 'xb', 0) as file:[](#l9.24)
file.write(b"test_posixpath.PathLikeTests")[](#l9.25)
- def assertPathEqual(self, func):
self.assertEqual(func(self.file_path), func(self.file_name))[](#l9.28)
- def test_path_join(self):
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),[](#l9.37)
self.path.join('a', 'b', 'c'))[](#l9.38)
- def test_path_commonpath(self):
common_path = self.path.commonpath([self.file_path, self.file_name])[](#l9.83)
self.assertEqual(common_path, self.file_name)[](#l9.84)
+ + if name=="main": unittest.main()
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -142,7 +142,10 @@ Core and Builtins Library ------- -- Issue 27598: Add Collections to collections.abc. +- Issue #26027, #27524: Add PEP 519/fspath() support to the os and os.path
- modules. Includes code from Jelle Zijlstra. + +- Issue #27598: Add Collections to collections.abc. Patch by Ivan Levkivskyi, docs by Neil Girdhar.
--- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -834,8 +834,11 @@ static int path_converter(PyObject *o, void *p) { path_t *path = (path_t *)p;
- PyObject *bytes, *to_cleanup = NULL; Py_ssize_t length;
- int is_index, is_buffer, is_bytes, is_unicode;
- /* Default to failure, forcing explicit signaling of succcess. */
- int ret = 0; const char *narrow;
#define FORMAT_EXCEPTION(exc, fmt) [](#l11.15) @@ -850,7 +853,7 @@ path_converter(PyObject *o, void *p) return 1; }
- /* Ensure it's always safe to call path_cleanup(). */ path->cleanup = NULL; if ((o == Py_None) && path->nullable) { @@ -862,21 +865,54 @@ path_converter(PyObject *o, void *p) return 1; }
- /* Only call this here so that we don't treat the return value of
os.fspath() as an fd or buffer. */[](#l11.31)
- is_index = path->allow_fd && PyIndex_Check(o);
- is_buffer = PyObject_CheckBuffer(o);
- is_bytes = PyBytes_Check(o);
- is_unicode = PyUnicode_Check(o);
- if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
/* Inline PyOS_FSPath() for better error messages. */[](#l11.38)
_Py_IDENTIFIER(__fspath__);[](#l11.39)
PyObject *func = NULL;[](#l11.40)
func = _PyObject_LookupSpecial(o, &PyId___fspath__);[](#l11.42)
if (NULL == func) {[](#l11.43)
goto error_exit;[](#l11.44)
}[](#l11.45)
o = to_cleanup = PyObject_CallFunctionObjArgs(func, NULL);[](#l11.47)
Py_DECREF(func);[](#l11.48)
if (NULL == o) {[](#l11.49)
goto error_exit;[](#l11.50)
}[](#l11.51)
else if (PyUnicode_Check(o)) {[](#l11.52)
is_unicode = 1;[](#l11.53)
}[](#l11.54)
else if (PyBytes_Check(o)) {[](#l11.55)
is_bytes = 1;[](#l11.56)
}[](#l11.57)
else {[](#l11.58)
goto error_exit;[](#l11.59)
}[](#l11.60)
- }
#ifdef MS_WINDOWS const wchar_t *wide; wide = PyUnicode_AsUnicodeAndSize(o, &length); if (!wide) {
return 0;[](#l11.69)
goto exit;[](#l11.70) }[](#l11.71) if (length > 32767) {[](#l11.72) FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");[](#l11.73)
return 0;[](#l11.74)
goto exit;[](#l11.75) }[](#l11.76) if (wcslen(wide) != length) {[](#l11.77) FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");[](#l11.78)
return 0;[](#l11.79)
goto exit;[](#l11.80) }[](#l11.81)
path->wide = wide; @@ -884,66 +920,71 @@ path_converter(PyObject *o, void *p) path->length = length; path->object = o; path->fd = -1;
return 1;[](#l11.88)
ret = 1;[](#l11.89)
goto exit;[](#l11.90)
#else if (!PyUnicode_FSConverter(o, &bytes)) {
return 0;[](#l11.93)
}[](#l11.94)
goto exit;[](#l11.98)
}[](#l11.99)
#ifdef MS_WINDOWS if (win32_warn_bytes_api()) {
return 0;[](#l11.105)
goto exit;[](#l11.106) }[](#l11.107)
#endif bytes = o; Py_INCREF(bytes); }
- else if (is_buffer) { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "%s%s%s should be %s, not %.200s", path->function_name ? path->function_name : "", path->function_name ? ": " : "", path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, integer or None" :[](#l11.119)
path->allow_fd ? "string, bytes or integer" :[](#l11.120)
path->nullable ? "string, bytes or None" :[](#l11.121)
"string or bytes",[](#l11.122)
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "[](#l11.123)
"integer or None" :[](#l11.124)
path->allow_fd ? "string, bytes, os.PathLike or integer" :[](#l11.125)
path->nullable ? "string, bytes, os.PathLike or None" :[](#l11.126)
"string, bytes or os.PathLike",[](#l11.127) Py_TYPE(o)->tp_name)) {[](#l11.128)
return 0;[](#l11.129)
goto exit;[](#l11.130) }[](#l11.131)
#ifdef MS_WINDOWS if (win32_warn_bytes_api()) {
return 0;[](#l11.134)
goto exit;[](#l11.135) }[](#l11.136)
#endif bytes = PyBytes_FromObject(o); if (!bytes) {
return 0;[](#l11.140)
} else if (path->allow_fd && PyIndex_Check(o)) { if (!_fd_converter(o, &path->fd)) {goto exit;[](#l11.141) }[](#l11.142)
return 0;[](#l11.146)
goto exit;[](#l11.147) }[](#l11.148) path->wide = NULL;[](#l11.149) path->narrow = NULL;[](#l11.150) path->length = 0;[](#l11.151) path->object = o;[](#l11.152)
return 1;[](#l11.153)
ret = 1;[](#l11.154)
} else {goto exit;[](#l11.155)
- error_exit: PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s", path->function_name ? path->function_name : "", path->function_name ? ": " : "", path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, integer or None" :[](#l11.163)
path->allow_fd ? "string, bytes or integer" :[](#l11.164)
path->nullable ? "string, bytes or None" :[](#l11.165)
"string or bytes",[](#l11.166)
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "[](#l11.167)
"integer or None" :[](#l11.168)
path->allow_fd ? "string, bytes, os.PathLike or integer" :[](#l11.169)
path->nullable ? "string, bytes, os.PathLike or None" :[](#l11.170)
"string, bytes or os.PathLike",[](#l11.171) Py_TYPE(o)->tp_name);[](#l11.172)
return 0;[](#l11.173)
} length = PyBytes_GET_SIZE(bytes); @@ -951,7 +992,7 @@ path_converter(PyObject *o, void *p) if (length > MAX_PATH-1) { FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); Py_DECREF(bytes);goto exit;[](#l11.174)
return 0;[](#l11.182)
#endif @@ -959,7 +1000,7 @@ path_converter(PyObject *o, void *p) if ((size_t)length != strlen(narrow)) { FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); Py_DECREF(bytes);
return 0;[](#l11.191)
} path->wide = NULL; @@ -969,12 +1010,15 @@ path_converter(PyObject *o, void *p) path->fd = -1; if (bytes == o) { Py_DECREF(bytes);goto exit;[](#l11.192)
return 1;[](#l11.200)
} static void @@ -12329,6 +12373,8 @@ error: PyObject * PyOS_FSPath(PyObject *path) {