mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
os.path. Thanks to Jelle Zijlstra for the initial patch against posixmodule.c.
This commit is contained in:
parent
6ed442c48d
commit
3f9183b5ac
11 changed files with 424 additions and 52 deletions
|
@ -69,6 +69,12 @@ def getctime(filename):
|
||||||
def commonprefix(m):
|
def commonprefix(m):
|
||||||
"Given a list of pathnames, returns the longest common leading component"
|
"Given a list of pathnames, returns the longest common leading component"
|
||||||
if not m: return ''
|
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)):
|
||||||
|
m = tuple(map(os.fspath, m))
|
||||||
s1 = min(m)
|
s1 = min(m)
|
||||||
s2 = max(m)
|
s2 = max(m)
|
||||||
for i, c in enumerate(s1):
|
for i, c in enumerate(s1):
|
||||||
|
|
|
@ -46,6 +46,7 @@ def normcase(s):
|
||||||
"""Normalize case of pathname.
|
"""Normalize case of pathname.
|
||||||
|
|
||||||
Makes all characters lowercase and all slashes into backslashes."""
|
Makes all characters lowercase and all slashes into backslashes."""
|
||||||
|
s = os.fspath(s)
|
||||||
try:
|
try:
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
return s.replace(b'/', b'\\').lower()
|
return s.replace(b'/', b'\\').lower()
|
||||||
|
@ -66,12 +67,14 @@ def normcase(s):
|
||||||
|
|
||||||
def isabs(s):
|
def isabs(s):
|
||||||
"""Test whether a path is absolute"""
|
"""Test whether a path is absolute"""
|
||||||
|
s = os.fspath(s)
|
||||||
s = splitdrive(s)[1]
|
s = splitdrive(s)[1]
|
||||||
return len(s) > 0 and s[0] in _get_bothseps(s)
|
return len(s) > 0 and s[0] in _get_bothseps(s)
|
||||||
|
|
||||||
|
|
||||||
# Join two (or more) paths.
|
# Join two (or more) paths.
|
||||||
def join(path, *paths):
|
def join(path, *paths):
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
sep = b'\\'
|
sep = b'\\'
|
||||||
seps = b'\\/'
|
seps = b'\\/'
|
||||||
|
@ -84,7 +87,7 @@ def join(path, *paths):
|
||||||
if not paths:
|
if not paths:
|
||||||
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
||||||
result_drive, result_path = splitdrive(path)
|
result_drive, result_path = splitdrive(path)
|
||||||
for p in paths:
|
for p in map(os.fspath, paths):
|
||||||
p_drive, p_path = splitdrive(p)
|
p_drive, p_path = splitdrive(p)
|
||||||
if p_path and p_path[0] in seps:
|
if p_path and p_path[0] in seps:
|
||||||
# Second path is absolute
|
# Second path is absolute
|
||||||
|
@ -136,6 +139,7 @@ def splitdrive(p):
|
||||||
Paths cannot contain both a drive letter and a UNC path.
|
Paths cannot contain both a drive letter and a UNC path.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
p = os.fspath(p)
|
||||||
if len(p) >= 2:
|
if len(p) >= 2:
|
||||||
if isinstance(p, bytes):
|
if isinstance(p, bytes):
|
||||||
sep = b'\\'
|
sep = b'\\'
|
||||||
|
@ -199,7 +203,7 @@ def split(p):
|
||||||
|
|
||||||
Return tuple (head, tail) where tail is everything after the final slash.
|
Return tuple (head, tail) where tail is everything after the final slash.
|
||||||
Either part may be empty."""
|
Either part may be empty."""
|
||||||
|
p = os.fspath(p)
|
||||||
seps = _get_bothseps(p)
|
seps = _get_bothseps(p)
|
||||||
d, p = splitdrive(p)
|
d, p = splitdrive(p)
|
||||||
# set i to index beyond p's last slash
|
# set i to index beyond p's last slash
|
||||||
|
@ -218,6 +222,7 @@ def split(p):
|
||||||
# It is always true that root + ext == p.
|
# It is always true that root + ext == p.
|
||||||
|
|
||||||
def splitext(p):
|
def splitext(p):
|
||||||
|
p = os.fspath(p)
|
||||||
if isinstance(p, bytes):
|
if isinstance(p, bytes):
|
||||||
return genericpath._splitext(p, b'\\', b'/', b'.')
|
return genericpath._splitext(p, b'\\', b'/', b'.')
|
||||||
else:
|
else:
|
||||||
|
@ -278,6 +283,7 @@ except ImportError:
|
||||||
def ismount(path):
|
def ismount(path):
|
||||||
"""Test whether a path is a mount point (a drive root, the root of a
|
"""Test whether a path is a mount point (a drive root, the root of a
|
||||||
share, or a mounted volume)"""
|
share, or a mounted volume)"""
|
||||||
|
path = os.fspath(path)
|
||||||
seps = _get_bothseps(path)
|
seps = _get_bothseps(path)
|
||||||
path = abspath(path)
|
path = abspath(path)
|
||||||
root, rest = splitdrive(path)
|
root, rest = splitdrive(path)
|
||||||
|
@ -305,6 +311,7 @@ def expanduser(path):
|
||||||
"""Expand ~ and ~user constructs.
|
"""Expand ~ and ~user constructs.
|
||||||
|
|
||||||
If user or $HOME is unknown, do nothing."""
|
If user or $HOME is unknown, do nothing."""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
tilde = b'~'
|
tilde = b'~'
|
||||||
else:
|
else:
|
||||||
|
@ -354,6 +361,7 @@ def expandvars(path):
|
||||||
"""Expand shell variables of the forms $var, ${var} and %var%.
|
"""Expand shell variables of the forms $var, ${var} and %var%.
|
||||||
|
|
||||||
Unknown variables are left unchanged."""
|
Unknown variables are left unchanged."""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
if b'$' not in path and b'%' not in path:
|
if b'$' not in path and b'%' not in path:
|
||||||
return path
|
return path
|
||||||
|
@ -464,6 +472,7 @@ def expandvars(path):
|
||||||
|
|
||||||
def normpath(path):
|
def normpath(path):
|
||||||
"""Normalize path, eliminating double slashes, etc."""
|
"""Normalize path, eliminating double slashes, etc."""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
sep = b'\\'
|
sep = b'\\'
|
||||||
altsep = b'/'
|
altsep = b'/'
|
||||||
|
@ -518,6 +527,7 @@ try:
|
||||||
except ImportError: # not running on Windows - mock up something sensible
|
except ImportError: # not running on Windows - mock up something sensible
|
||||||
def abspath(path):
|
def abspath(path):
|
||||||
"""Return the absolute version of a path."""
|
"""Return the absolute version of a path."""
|
||||||
|
path = os.fspath(path)
|
||||||
if not isabs(path):
|
if not isabs(path):
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
cwd = os.getcwdb()
|
cwd = os.getcwdb()
|
||||||
|
@ -531,6 +541,7 @@ else: # use native Windows method on Windows
|
||||||
"""Return the absolute version of a path."""
|
"""Return the absolute version of a path."""
|
||||||
|
|
||||||
if path: # Empty path must return current working directory.
|
if path: # Empty path must return current working directory.
|
||||||
|
path = os.fspath(path)
|
||||||
try:
|
try:
|
||||||
path = _getfullpathname(path)
|
path = _getfullpathname(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -549,6 +560,7 @@ supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
|
||||||
|
|
||||||
def relpath(path, start=None):
|
def relpath(path, start=None):
|
||||||
"""Return a relative version of a path"""
|
"""Return a relative version of a path"""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
sep = b'\\'
|
sep = b'\\'
|
||||||
curdir = b'.'
|
curdir = b'.'
|
||||||
|
@ -564,6 +576,7 @@ def relpath(path, start=None):
|
||||||
if not path:
|
if not path:
|
||||||
raise ValueError("no path specified")
|
raise ValueError("no path specified")
|
||||||
|
|
||||||
|
start = os.fspath(start)
|
||||||
try:
|
try:
|
||||||
start_abs = abspath(normpath(start))
|
start_abs = abspath(normpath(start))
|
||||||
path_abs = abspath(normpath(path))
|
path_abs = abspath(normpath(path))
|
||||||
|
@ -607,6 +620,7 @@ def commonpath(paths):
|
||||||
if not paths:
|
if not paths:
|
||||||
raise ValueError('commonpath() arg is an empty sequence')
|
raise ValueError('commonpath() arg is an empty sequence')
|
||||||
|
|
||||||
|
paths = tuple(map(os.fspath, paths))
|
||||||
if isinstance(paths[0], bytes):
|
if isinstance(paths[0], bytes):
|
||||||
sep = b'\\'
|
sep = b'\\'
|
||||||
altsep = b'/'
|
altsep = b'/'
|
||||||
|
|
|
@ -353,7 +353,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
|
||||||
dirs.remove('CVS') # don't visit CVS directories
|
dirs.remove('CVS') # don't visit CVS directories
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
top = fspath(top)
|
||||||
dirs = []
|
dirs = []
|
||||||
nondirs = []
|
nondirs = []
|
||||||
walk_dirs = []
|
walk_dirs = []
|
||||||
|
@ -536,6 +536,8 @@ if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
|
||||||
if 'CVS' in dirs:
|
if 'CVS' in dirs:
|
||||||
dirs.remove('CVS') # don't visit CVS directories
|
dirs.remove('CVS') # don't visit CVS directories
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(top, int) or not hasattr(top, '__index__'):
|
||||||
|
top = fspath(top)
|
||||||
# Note: To guard against symlink races, we use the standard
|
# Note: To guard against symlink races, we use the standard
|
||||||
# lstat()/open()/fstat() trick.
|
# lstat()/open()/fstat() trick.
|
||||||
orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
|
orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
|
||||||
|
|
|
@ -49,6 +49,7 @@ def _get_sep(path):
|
||||||
|
|
||||||
def normcase(s):
|
def normcase(s):
|
||||||
"""Normalize case of pathname. Has no effect under Posix"""
|
"""Normalize case of pathname. Has no effect under Posix"""
|
||||||
|
s = os.fspath(s)
|
||||||
if not isinstance(s, (bytes, str)):
|
if not isinstance(s, (bytes, str)):
|
||||||
raise TypeError("normcase() argument must be str or bytes, "
|
raise TypeError("normcase() argument must be str or bytes, "
|
||||||
"not '{}'".format(s.__class__.__name__))
|
"not '{}'".format(s.__class__.__name__))
|
||||||
|
@ -60,6 +61,7 @@ def normcase(s):
|
||||||
|
|
||||||
def isabs(s):
|
def isabs(s):
|
||||||
"""Test whether a path is absolute"""
|
"""Test whether a path is absolute"""
|
||||||
|
s = os.fspath(s)
|
||||||
sep = _get_sep(s)
|
sep = _get_sep(s)
|
||||||
return s.startswith(sep)
|
return s.startswith(sep)
|
||||||
|
|
||||||
|
@ -73,12 +75,13 @@ def join(a, *p):
|
||||||
If any component is an absolute path, all previous path components
|
If any component is an absolute path, all previous path components
|
||||||
will be discarded. An empty last part will result in a path that
|
will be discarded. An empty last part will result in a path that
|
||||||
ends with a separator."""
|
ends with a separator."""
|
||||||
|
a = os.fspath(a)
|
||||||
sep = _get_sep(a)
|
sep = _get_sep(a)
|
||||||
path = a
|
path = a
|
||||||
try:
|
try:
|
||||||
if not p:
|
if not p:
|
||||||
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
||||||
for b in p:
|
for b in map(os.fspath, p):
|
||||||
if b.startswith(sep):
|
if b.startswith(sep):
|
||||||
path = b
|
path = b
|
||||||
elif not path or path.endswith(sep):
|
elif not path or path.endswith(sep):
|
||||||
|
@ -99,6 +102,7 @@ def join(a, *p):
|
||||||
def split(p):
|
def split(p):
|
||||||
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
|
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
|
||||||
everything after the final slash. Either part may be empty."""
|
everything after the final slash. Either part may be empty."""
|
||||||
|
p = os.fspath(p)
|
||||||
sep = _get_sep(p)
|
sep = _get_sep(p)
|
||||||
i = p.rfind(sep) + 1
|
i = p.rfind(sep) + 1
|
||||||
head, tail = p[:i], p[i:]
|
head, tail = p[:i], p[i:]
|
||||||
|
@ -113,6 +117,7 @@ def split(p):
|
||||||
# It is always true that root + ext == p.
|
# It is always true that root + ext == p.
|
||||||
|
|
||||||
def splitext(p):
|
def splitext(p):
|
||||||
|
p = os.fspath(p)
|
||||||
if isinstance(p, bytes):
|
if isinstance(p, bytes):
|
||||||
sep = b'/'
|
sep = b'/'
|
||||||
extsep = b'.'
|
extsep = b'.'
|
||||||
|
@ -128,6 +133,7 @@ splitext.__doc__ = genericpath._splitext.__doc__
|
||||||
def splitdrive(p):
|
def splitdrive(p):
|
||||||
"""Split a pathname into drive and path. On Posix, drive is always
|
"""Split a pathname into drive and path. On Posix, drive is always
|
||||||
empty."""
|
empty."""
|
||||||
|
p = os.fspath(p)
|
||||||
return p[:0], p
|
return p[:0], p
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,6 +141,7 @@ def splitdrive(p):
|
||||||
|
|
||||||
def basename(p):
|
def basename(p):
|
||||||
"""Returns the final component of a pathname"""
|
"""Returns the final component of a pathname"""
|
||||||
|
p = os.fspath(p)
|
||||||
sep = _get_sep(p)
|
sep = _get_sep(p)
|
||||||
i = p.rfind(sep) + 1
|
i = p.rfind(sep) + 1
|
||||||
return p[i:]
|
return p[i:]
|
||||||
|
@ -144,6 +151,7 @@ def basename(p):
|
||||||
|
|
||||||
def dirname(p):
|
def dirname(p):
|
||||||
"""Returns the directory component of a pathname"""
|
"""Returns the directory component of a pathname"""
|
||||||
|
p = os.fspath(p)
|
||||||
sep = _get_sep(p)
|
sep = _get_sep(p)
|
||||||
i = p.rfind(sep) + 1
|
i = p.rfind(sep) + 1
|
||||||
head = p[:i]
|
head = p[:i]
|
||||||
|
@ -222,6 +230,7 @@ def ismount(path):
|
||||||
def expanduser(path):
|
def expanduser(path):
|
||||||
"""Expand ~ and ~user constructions. If user or $HOME is unknown,
|
"""Expand ~ and ~user constructions. If user or $HOME is unknown,
|
||||||
do nothing."""
|
do nothing."""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
tilde = b'~'
|
tilde = b'~'
|
||||||
else:
|
else:
|
||||||
|
@ -267,6 +276,7 @@ _varprogb = None
|
||||||
def expandvars(path):
|
def expandvars(path):
|
||||||
"""Expand shell variables of form $var and ${var}. Unknown variables
|
"""Expand shell variables of form $var and ${var}. Unknown variables
|
||||||
are left unchanged."""
|
are left unchanged."""
|
||||||
|
path = os.fspath(path)
|
||||||
global _varprog, _varprogb
|
global _varprog, _varprogb
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
if b'$' not in path:
|
if b'$' not in path:
|
||||||
|
@ -318,6 +328,7 @@ def expandvars(path):
|
||||||
|
|
||||||
def normpath(path):
|
def normpath(path):
|
||||||
"""Normalize path, eliminating double slashes, etc."""
|
"""Normalize path, eliminating double slashes, etc."""
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
sep = b'/'
|
sep = b'/'
|
||||||
empty = b''
|
empty = b''
|
||||||
|
@ -355,6 +366,7 @@ def normpath(path):
|
||||||
|
|
||||||
def abspath(path):
|
def abspath(path):
|
||||||
"""Return an absolute path."""
|
"""Return an absolute path."""
|
||||||
|
path = os.fspath(path)
|
||||||
if not isabs(path):
|
if not isabs(path):
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
cwd = os.getcwdb()
|
cwd = os.getcwdb()
|
||||||
|
@ -370,6 +382,7 @@ def abspath(path):
|
||||||
def realpath(filename):
|
def realpath(filename):
|
||||||
"""Return the canonical path of the specified filename, eliminating any
|
"""Return the canonical path of the specified filename, eliminating any
|
||||||
symbolic links encountered in the path."""
|
symbolic links encountered in the path."""
|
||||||
|
filename = os.fspath(filename)
|
||||||
path, ok = _joinrealpath(filename[:0], filename, {})
|
path, ok = _joinrealpath(filename[:0], filename, {})
|
||||||
return abspath(path)
|
return abspath(path)
|
||||||
|
|
||||||
|
@ -434,6 +447,7 @@ def relpath(path, start=None):
|
||||||
if not path:
|
if not path:
|
||||||
raise ValueError("no path specified")
|
raise ValueError("no path specified")
|
||||||
|
|
||||||
|
path = os.fspath(path)
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
curdir = b'.'
|
curdir = b'.'
|
||||||
sep = b'/'
|
sep = b'/'
|
||||||
|
@ -445,6 +459,8 @@ def relpath(path, start=None):
|
||||||
|
|
||||||
if start is None:
|
if start is None:
|
||||||
start = curdir
|
start = curdir
|
||||||
|
else:
|
||||||
|
start = os.fspath(start)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start_list = [x for x in abspath(start).split(sep) if x]
|
start_list = [x for x in abspath(start).split(sep) if x]
|
||||||
|
@ -472,6 +488,7 @@ def commonpath(paths):
|
||||||
if not paths:
|
if not paths:
|
||||||
raise ValueError('commonpath() arg is an empty sequence')
|
raise ValueError('commonpath() arg is an empty sequence')
|
||||||
|
|
||||||
|
paths = tuple(map(os.fspath, paths))
|
||||||
if isinstance(paths[0], bytes):
|
if isinstance(paths[0], bytes):
|
||||||
sep = b'/'
|
sep = b'/'
|
||||||
curdir = b'.'
|
curdir = b'.'
|
||||||
|
|
|
@ -450,16 +450,15 @@ class CommonTest(GenericTest):
|
||||||
with self.assertRaisesRegex(TypeError, errmsg):
|
with self.assertRaisesRegex(TypeError, errmsg):
|
||||||
self.pathmodule.join('str', b'bytes')
|
self.pathmodule.join('str', b'bytes')
|
||||||
# regression, see #15377
|
# regression, see #15377
|
||||||
errmsg = r'join\(\) argument must be str or bytes, not %r'
|
with self.assertRaisesRegex(TypeError, 'int'):
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'int'):
|
|
||||||
self.pathmodule.join(42, 'str')
|
self.pathmodule.join(42, 'str')
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'int'):
|
with self.assertRaisesRegex(TypeError, 'int'):
|
||||||
self.pathmodule.join('str', 42)
|
self.pathmodule.join('str', 42)
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'int'):
|
with self.assertRaisesRegex(TypeError, 'int'):
|
||||||
self.pathmodule.join(42)
|
self.pathmodule.join(42)
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'list'):
|
with self.assertRaisesRegex(TypeError, 'list'):
|
||||||
self.pathmodule.join([])
|
self.pathmodule.join([])
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'):
|
with self.assertRaisesRegex(TypeError, 'bytearray'):
|
||||||
self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar'))
|
self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar'))
|
||||||
|
|
||||||
def test_relpath_errors(self):
|
def test_relpath_errors(self):
|
||||||
|
@ -471,14 +470,59 @@ class CommonTest(GenericTest):
|
||||||
self.pathmodule.relpath(b'bytes', 'str')
|
self.pathmodule.relpath(b'bytes', 'str')
|
||||||
with self.assertRaisesRegex(TypeError, errmsg):
|
with self.assertRaisesRegex(TypeError, errmsg):
|
||||||
self.pathmodule.relpath('str', b'bytes')
|
self.pathmodule.relpath('str', b'bytes')
|
||||||
errmsg = r'relpath\(\) argument must be str or bytes, not %r'
|
with self.assertRaisesRegex(TypeError, 'int'):
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'int'):
|
|
||||||
self.pathmodule.relpath(42, 'str')
|
self.pathmodule.relpath(42, 'str')
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'int'):
|
with self.assertRaisesRegex(TypeError, 'int'):
|
||||||
self.pathmodule.relpath('str', 42)
|
self.pathmodule.relpath('str', 42)
|
||||||
with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'):
|
with self.assertRaisesRegex(TypeError, 'bytearray'):
|
||||||
self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar'))
|
self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar'))
|
||||||
|
|
||||||
|
|
||||||
|
class PathLikeTests(unittest.TestCase):
|
||||||
|
|
||||||
|
class PathLike:
|
||||||
|
def __init__(self, path=''):
|
||||||
|
self.path = path
|
||||||
|
def __fspath__(self):
|
||||||
|
if isinstance(self.path, BaseException):
|
||||||
|
raise self.path
|
||||||
|
else:
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.file_name = support.TESTFN.lower()
|
||||||
|
self.file_path = self.PathLike(support.TESTFN)
|
||||||
|
self.addCleanup(support.unlink, self.file_name)
|
||||||
|
create_file(self.file_name, b"test_genericpath.PathLikeTests")
|
||||||
|
|
||||||
|
def assertPathEqual(self, func):
|
||||||
|
self.assertEqual(func(self.file_path), func(self.file_name))
|
||||||
|
|
||||||
|
def test_path_exists(self):
|
||||||
|
self.assertPathEqual(os.path.exists)
|
||||||
|
|
||||||
|
def test_path_isfile(self):
|
||||||
|
self.assertPathEqual(os.path.isfile)
|
||||||
|
|
||||||
|
def test_path_isdir(self):
|
||||||
|
self.assertPathEqual(os.path.isdir)
|
||||||
|
|
||||||
|
def test_path_commonprefix(self):
|
||||||
|
self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]),
|
||||||
|
self.file_name)
|
||||||
|
|
||||||
|
def test_path_getsize(self):
|
||||||
|
self.assertPathEqual(os.path.getsize)
|
||||||
|
|
||||||
|
def test_path_getmtime(self):
|
||||||
|
self.assertPathEqual(os.path.getatime)
|
||||||
|
|
||||||
|
def test_path_getctime(self):
|
||||||
|
self.assertPathEqual(os.path.getctime)
|
||||||
|
|
||||||
|
def test_path_samefile(self):
|
||||||
|
self.assertTrue(os.path.samefile(self.file_path, self.file_name))
|
||||||
|
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -452,5 +452,88 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
|
||||||
attributes = ['relpath', 'splitunc']
|
attributes = ['relpath', 'splitunc']
|
||||||
|
|
||||||
|
|
||||||
|
class PathLikeTests(unittest.TestCase):
|
||||||
|
|
||||||
|
path = ntpath
|
||||||
|
|
||||||
|
class PathLike:
|
||||||
|
def __init__(self, path=''):
|
||||||
|
self.path = path
|
||||||
|
def __fspath__(self):
|
||||||
|
if isinstance(self.path, BaseException):
|
||||||
|
raise self.path
|
||||||
|
else:
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.file_name = support.TESTFN.lower()
|
||||||
|
self.file_path = self.PathLike(support.TESTFN)
|
||||||
|
self.addCleanup(support.unlink, self.file_name)
|
||||||
|
with open(self.file_name, 'xb', 0) as file:
|
||||||
|
file.write(b"test_ntpath.PathLikeTests")
|
||||||
|
|
||||||
|
def assertPathEqual(self, func):
|
||||||
|
self.assertEqual(func(self.file_path), func(self.file_name))
|
||||||
|
|
||||||
|
def test_path_normcase(self):
|
||||||
|
self.assertPathEqual(self.path.normcase)
|
||||||
|
|
||||||
|
def test_path_isabs(self):
|
||||||
|
self.assertPathEqual(self.path.isabs)
|
||||||
|
|
||||||
|
def test_path_join(self):
|
||||||
|
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),
|
||||||
|
self.path.join('a', 'b', 'c'))
|
||||||
|
|
||||||
|
def test_path_split(self):
|
||||||
|
self.assertPathEqual(self.path.split)
|
||||||
|
|
||||||
|
def test_path_splitext(self):
|
||||||
|
self.assertPathEqual(self.path.splitext)
|
||||||
|
|
||||||
|
def test_path_splitdrive(self):
|
||||||
|
self.assertPathEqual(self.path.splitdrive)
|
||||||
|
|
||||||
|
def test_path_basename(self):
|
||||||
|
self.assertPathEqual(self.path.basename)
|
||||||
|
|
||||||
|
def test_path_dirname(self):
|
||||||
|
self.assertPathEqual(self.path.dirname)
|
||||||
|
|
||||||
|
def test_path_islink(self):
|
||||||
|
self.assertPathEqual(self.path.islink)
|
||||||
|
|
||||||
|
def test_path_lexists(self):
|
||||||
|
self.assertPathEqual(self.path.lexists)
|
||||||
|
|
||||||
|
def test_path_ismount(self):
|
||||||
|
self.assertPathEqual(self.path.ismount)
|
||||||
|
|
||||||
|
def test_path_expanduser(self):
|
||||||
|
self.assertPathEqual(self.path.expanduser)
|
||||||
|
|
||||||
|
def test_path_expandvars(self):
|
||||||
|
self.assertPathEqual(self.path.expandvars)
|
||||||
|
|
||||||
|
def test_path_normpath(self):
|
||||||
|
self.assertPathEqual(self.path.normpath)
|
||||||
|
|
||||||
|
def test_path_abspath(self):
|
||||||
|
self.assertPathEqual(self.path.abspath)
|
||||||
|
|
||||||
|
def test_path_realpath(self):
|
||||||
|
self.assertPathEqual(self.path.realpath)
|
||||||
|
|
||||||
|
def test_path_relpath(self):
|
||||||
|
self.assertPathEqual(self.path.relpath)
|
||||||
|
|
||||||
|
def test_path_commonpath(self):
|
||||||
|
common_path = self.path.commonpath([self.file_path, self.file_name])
|
||||||
|
self.assertEqual(common_path, self.file_name)
|
||||||
|
|
||||||
|
def test_path_isdir(self):
|
||||||
|
self.assertPathEqual(self.path.isdir)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -874,10 +874,12 @@ class WalkTests(unittest.TestCase):
|
||||||
self.assertEqual(all[2 + flipped], (self.sub11_path, [], []))
|
self.assertEqual(all[2 + flipped], (self.sub11_path, [], []))
|
||||||
self.assertEqual(all[3 - 2 * flipped], self.sub2_tree)
|
self.assertEqual(all[3 - 2 * flipped], self.sub2_tree)
|
||||||
|
|
||||||
def test_walk_prune(self):
|
def test_walk_prune(self, walk_path=None):
|
||||||
|
if walk_path is None:
|
||||||
|
walk_path = self.walk_path
|
||||||
# Prune the search.
|
# Prune the search.
|
||||||
all = []
|
all = []
|
||||||
for root, dirs, files in self.walk(self.walk_path):
|
for root, dirs, files in self.walk(walk_path):
|
||||||
all.append((root, dirs, files))
|
all.append((root, dirs, files))
|
||||||
# Don't descend into SUB1.
|
# Don't descend into SUB1.
|
||||||
if 'SUB1' in dirs:
|
if 'SUB1' in dirs:
|
||||||
|
@ -886,11 +888,22 @@ class WalkTests(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(len(all), 2)
|
self.assertEqual(len(all), 2)
|
||||||
self.assertEqual(all[0],
|
self.assertEqual(all[0],
|
||||||
(self.walk_path, ["SUB2"], ["tmp1"]))
|
(str(walk_path), ["SUB2"], ["tmp1"]))
|
||||||
|
|
||||||
all[1][-1].sort()
|
all[1][-1].sort()
|
||||||
self.assertEqual(all[1], self.sub2_tree)
|
self.assertEqual(all[1], self.sub2_tree)
|
||||||
|
|
||||||
|
def test_file_like_path(self):
|
||||||
|
class FileLike:
|
||||||
|
def __init__(self, path):
|
||||||
|
self._path = path
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._path)
|
||||||
|
def __fspath__(self):
|
||||||
|
return self._path
|
||||||
|
|
||||||
|
self.test_walk_prune(FileLike(self.walk_path))
|
||||||
|
|
||||||
def test_walk_bottom_up(self):
|
def test_walk_bottom_up(self):
|
||||||
# Walk bottom-up.
|
# Walk bottom-up.
|
||||||
all = list(self.walk(self.walk_path, topdown=False))
|
all = list(self.walk(self.walk_path, topdown=False))
|
||||||
|
@ -2807,6 +2820,70 @@ class FDInheritanceTests(unittest.TestCase):
|
||||||
self.assertEqual(os.get_inheritable(slave_fd), False)
|
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),
|
||||||
|
('lstat', False, (), None),
|
||||||
|
('access', True, (os.F_OK,), None),
|
||||||
|
('chflags', False, (0,), None),
|
||||||
|
('lchflags', False, (0,), None),
|
||||||
|
('open', False, (0,), getattr(os, 'close', None)),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_path_t_converter(self):
|
||||||
|
class PathLike:
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __fspath__(self):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
str_filename = support.TESTFN
|
||||||
|
bytes_filename = support.TESTFN.encode('ascii')
|
||||||
|
bytearray_filename = bytearray(bytes_filename)
|
||||||
|
fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.addCleanup(os.close, fd)
|
||||||
|
self.addCleanup(support.unlink, support.TESTFN)
|
||||||
|
|
||||||
|
int_fspath = PathLike(fd)
|
||||||
|
str_fspath = PathLike(str_filename)
|
||||||
|
bytes_fspath = PathLike(bytes_filename)
|
||||||
|
bytearray_fspath = PathLike(bytearray_filename)
|
||||||
|
|
||||||
|
for name, allow_fd, extra_args, cleanup_fn in self.functions:
|
||||||
|
with self.subTest(name=name):
|
||||||
|
try:
|
||||||
|
fn = getattr(os, name)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for path in (str_filename, bytes_filename, bytearray_filename,
|
||||||
|
str_fspath, bytes_fspath):
|
||||||
|
with self.subTest(name=name, path=path):
|
||||||
|
result = fn(path, *extra_args)
|
||||||
|
if cleanup_fn is not None:
|
||||||
|
cleanup_fn(result)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError, 'should be string, bytes'):
|
||||||
|
fn(int_fspath, *extra_args)
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError, 'should be string, bytes'):
|
||||||
|
fn(bytearray_fspath, *extra_args)
|
||||||
|
|
||||||
|
if allow_fd:
|
||||||
|
result = fn(fd, *extra_args) # should not fail
|
||||||
|
if cleanup_fn is not None:
|
||||||
|
cleanup_fn(result)
|
||||||
|
else:
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
'os.PathLike'):
|
||||||
|
fn(fd, *extra_args)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, 'get_blocking'),
|
@unittest.skipUnless(hasattr(os, 'get_blocking'),
|
||||||
'needs os.get_blocking() and os.set_blocking()')
|
'needs os.get_blocking() and os.set_blocking()')
|
||||||
class BlockingTests(unittest.TestCase):
|
class BlockingTests(unittest.TestCase):
|
||||||
|
|
|
@ -397,7 +397,7 @@ class PosixTester(unittest.TestCase):
|
||||||
self.assertTrue(posix.stat(fp.fileno()))
|
self.assertTrue(posix.stat(fp.fileno()))
|
||||||
|
|
||||||
self.assertRaisesRegex(TypeError,
|
self.assertRaisesRegex(TypeError,
|
||||||
'should be string, bytes or integer, not',
|
'should be string, bytes, os.PathLike or integer, not',
|
||||||
posix.stat, float(fp.fileno()))
|
posix.stat, float(fp.fileno()))
|
||||||
finally:
|
finally:
|
||||||
fp.close()
|
fp.close()
|
||||||
|
@ -409,16 +409,16 @@ class PosixTester(unittest.TestCase):
|
||||||
self.assertTrue(posix.stat(os.fsencode(support.TESTFN)))
|
self.assertTrue(posix.stat(os.fsencode(support.TESTFN)))
|
||||||
|
|
||||||
self.assertWarnsRegex(DeprecationWarning,
|
self.assertWarnsRegex(DeprecationWarning,
|
||||||
'should be string, bytes or integer, not',
|
'should be string, bytes, os.PathLike or integer, not',
|
||||||
posix.stat, bytearray(os.fsencode(support.TESTFN)))
|
posix.stat, bytearray(os.fsencode(support.TESTFN)))
|
||||||
self.assertRaisesRegex(TypeError,
|
self.assertRaisesRegex(TypeError,
|
||||||
'should be string, bytes or integer, not',
|
'should be string, bytes, os.PathLike or integer, not',
|
||||||
posix.stat, None)
|
posix.stat, None)
|
||||||
self.assertRaisesRegex(TypeError,
|
self.assertRaisesRegex(TypeError,
|
||||||
'should be string, bytes or integer, not',
|
'should be string, bytes, os.PathLike or integer, not',
|
||||||
posix.stat, list(support.TESTFN))
|
posix.stat, list(support.TESTFN))
|
||||||
self.assertRaisesRegex(TypeError,
|
self.assertRaisesRegex(TypeError,
|
||||||
'should be string, bytes or integer, not',
|
'should be string, bytes, os.PathLike or integer, not',
|
||||||
posix.stat, list(os.fsencode(support.TESTFN)))
|
posix.stat, list(os.fsencode(support.TESTFN)))
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()")
|
@unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()")
|
||||||
|
|
|
@ -596,5 +596,85 @@ class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase):
|
||||||
attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
|
attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
|
||||||
|
|
||||||
|
|
||||||
|
class PathLikeTests(unittest.TestCase):
|
||||||
|
|
||||||
|
path = posixpath
|
||||||
|
|
||||||
|
class PathLike:
|
||||||
|
def __init__(self, path=''):
|
||||||
|
self.path = path
|
||||||
|
def __fspath__(self):
|
||||||
|
if isinstance(self.path, BaseException):
|
||||||
|
raise self.path
|
||||||
|
else:
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.file_name = support.TESTFN.lower()
|
||||||
|
self.file_path = self.PathLike(support.TESTFN)
|
||||||
|
self.addCleanup(support.unlink, self.file_name)
|
||||||
|
with open(self.file_name, 'xb', 0) as file:
|
||||||
|
file.write(b"test_posixpath.PathLikeTests")
|
||||||
|
|
||||||
|
def assertPathEqual(self, func):
|
||||||
|
self.assertEqual(func(self.file_path), func(self.file_name))
|
||||||
|
|
||||||
|
def test_path_normcase(self):
|
||||||
|
self.assertPathEqual(self.path.normcase)
|
||||||
|
|
||||||
|
def test_path_isabs(self):
|
||||||
|
self.assertPathEqual(self.path.isabs)
|
||||||
|
|
||||||
|
def test_path_join(self):
|
||||||
|
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),
|
||||||
|
self.path.join('a', 'b', 'c'))
|
||||||
|
|
||||||
|
def test_path_split(self):
|
||||||
|
self.assertPathEqual(self.path.split)
|
||||||
|
|
||||||
|
def test_path_splitext(self):
|
||||||
|
self.assertPathEqual(self.path.splitext)
|
||||||
|
|
||||||
|
def test_path_splitdrive(self):
|
||||||
|
self.assertPathEqual(self.path.splitdrive)
|
||||||
|
|
||||||
|
def test_path_basename(self):
|
||||||
|
self.assertPathEqual(self.path.basename)
|
||||||
|
|
||||||
|
def test_path_dirname(self):
|
||||||
|
self.assertPathEqual(self.path.dirname)
|
||||||
|
|
||||||
|
def test_path_islink(self):
|
||||||
|
self.assertPathEqual(self.path.islink)
|
||||||
|
|
||||||
|
def test_path_lexists(self):
|
||||||
|
self.assertPathEqual(self.path.lexists)
|
||||||
|
|
||||||
|
def test_path_ismount(self):
|
||||||
|
self.assertPathEqual(self.path.ismount)
|
||||||
|
|
||||||
|
def test_path_expanduser(self):
|
||||||
|
self.assertPathEqual(self.path.expanduser)
|
||||||
|
|
||||||
|
def test_path_expandvars(self):
|
||||||
|
self.assertPathEqual(self.path.expandvars)
|
||||||
|
|
||||||
|
def test_path_normpath(self):
|
||||||
|
self.assertPathEqual(self.path.normpath)
|
||||||
|
|
||||||
|
def test_path_abspath(self):
|
||||||
|
self.assertPathEqual(self.path.abspath)
|
||||||
|
|
||||||
|
def test_path_realpath(self):
|
||||||
|
self.assertPathEqual(self.path.realpath)
|
||||||
|
|
||||||
|
def test_path_relpath(self):
|
||||||
|
self.assertPathEqual(self.path.relpath)
|
||||||
|
|
||||||
|
def test_path_commonpath(self):
|
||||||
|
common_path = self.path.commonpath([self.file_path, self.file_name])
|
||||||
|
self.assertEqual(common_path, self.file_name)
|
||||||
|
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -142,7 +142,10 @@ Core and Builtins
|
||||||
Library
|
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.
|
Patch by Ivan Levkivskyi, docs by Neil Girdhar.
|
||||||
|
|
||||||
- Issue #25958: Support "anti-registration" of special methods from
|
- Issue #25958: Support "anti-registration" of special methods from
|
||||||
|
|
|
@ -834,8 +834,11 @@ static int
|
||||||
path_converter(PyObject *o, void *p)
|
path_converter(PyObject *o, void *p)
|
||||||
{
|
{
|
||||||
path_t *path = (path_t *)p;
|
path_t *path = (path_t *)p;
|
||||||
PyObject *bytes;
|
PyObject *bytes, *to_cleanup = NULL;
|
||||||
Py_ssize_t length;
|
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;
|
const char *narrow;
|
||||||
|
|
||||||
#define FORMAT_EXCEPTION(exc, fmt) \
|
#define FORMAT_EXCEPTION(exc, fmt) \
|
||||||
|
@ -850,7 +853,7 @@ path_converter(PyObject *o, void *p)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ensure it's always safe to call path_cleanup() */
|
/* Ensure it's always safe to call path_cleanup(). */
|
||||||
path->cleanup = NULL;
|
path->cleanup = NULL;
|
||||||
|
|
||||||
if ((o == Py_None) && path->nullable) {
|
if ((o == Py_None) && path->nullable) {
|
||||||
|
@ -862,21 +865,54 @@ path_converter(PyObject *o, void *p)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyUnicode_Check(o)) {
|
/* Only call this here so that we don't treat the return value of
|
||||||
|
os.fspath() as an fd or buffer. */
|
||||||
|
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. */
|
||||||
|
_Py_IDENTIFIER(__fspath__);
|
||||||
|
PyObject *func = NULL;
|
||||||
|
|
||||||
|
func = _PyObject_LookupSpecial(o, &PyId___fspath__);
|
||||||
|
if (NULL == func) {
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = to_cleanup = PyObject_CallFunctionObjArgs(func, NULL);
|
||||||
|
Py_DECREF(func);
|
||||||
|
if (NULL == o) {
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
else if (PyUnicode_Check(o)) {
|
||||||
|
is_unicode = 1;
|
||||||
|
}
|
||||||
|
else if (PyBytes_Check(o)) {
|
||||||
|
is_bytes = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_unicode) {
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
const wchar_t *wide;
|
const wchar_t *wide;
|
||||||
|
|
||||||
wide = PyUnicode_AsUnicodeAndSize(o, &length);
|
wide = PyUnicode_AsUnicodeAndSize(o, &length);
|
||||||
if (!wide) {
|
if (!wide) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (length > 32767) {
|
if (length > 32767) {
|
||||||
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
|
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (wcslen(wide) != length) {
|
if (wcslen(wide) != length) {
|
||||||
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
|
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
path->wide = wide;
|
path->wide = wide;
|
||||||
|
@ -884,66 +920,71 @@ path_converter(PyObject *o, void *p)
|
||||||
path->length = length;
|
path->length = length;
|
||||||
path->object = o;
|
path->object = o;
|
||||||
path->fd = -1;
|
path->fd = -1;
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto exit;
|
||||||
#else
|
#else
|
||||||
if (!PyUnicode_FSConverter(o, &bytes)) {
|
if (!PyUnicode_FSConverter(o, &bytes)) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (PyBytes_Check(o)) {
|
else if (is_bytes) {
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
if (win32_warn_bytes_api()) {
|
if (win32_warn_bytes_api()) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
bytes = o;
|
bytes = o;
|
||||||
Py_INCREF(bytes);
|
Py_INCREF(bytes);
|
||||||
}
|
}
|
||||||
else if (PyObject_CheckBuffer(o)) {
|
else if (is_buffer) {
|
||||||
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
|
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
|
||||||
"%s%s%s should be %s, not %.200s",
|
"%s%s%s should be %s, not %.200s",
|
||||||
path->function_name ? path->function_name : "",
|
path->function_name ? path->function_name : "",
|
||||||
path->function_name ? ": " : "",
|
path->function_name ? ": " : "",
|
||||||
path->argument_name ? path->argument_name : "path",
|
path->argument_name ? path->argument_name : "path",
|
||||||
path->allow_fd && path->nullable ? "string, bytes, integer or None" :
|
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
|
||||||
path->allow_fd ? "string, bytes or integer" :
|
"integer or None" :
|
||||||
path->nullable ? "string, bytes or None" :
|
path->allow_fd ? "string, bytes, os.PathLike or integer" :
|
||||||
"string or bytes",
|
path->nullable ? "string, bytes, os.PathLike or None" :
|
||||||
|
"string, bytes or os.PathLike",
|
||||||
Py_TYPE(o)->tp_name)) {
|
Py_TYPE(o)->tp_name)) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
if (win32_warn_bytes_api()) {
|
if (win32_warn_bytes_api()) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
bytes = PyBytes_FromObject(o);
|
bytes = PyBytes_FromObject(o);
|
||||||
if (!bytes) {
|
if (!bytes) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (path->allow_fd && PyIndex_Check(o)) {
|
else if (path->allow_fd && PyIndex_Check(o)) {
|
||||||
if (!_fd_converter(o, &path->fd)) {
|
if (!_fd_converter(o, &path->fd)) {
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
path->wide = NULL;
|
path->wide = NULL;
|
||||||
path->narrow = NULL;
|
path->narrow = NULL;
|
||||||
path->length = 0;
|
path->length = 0;
|
||||||
path->object = o;
|
path->object = o;
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
error_exit:
|
||||||
PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s",
|
PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s",
|
||||||
path->function_name ? path->function_name : "",
|
path->function_name ? path->function_name : "",
|
||||||
path->function_name ? ": " : "",
|
path->function_name ? ": " : "",
|
||||||
path->argument_name ? path->argument_name : "path",
|
path->argument_name ? path->argument_name : "path",
|
||||||
path->allow_fd && path->nullable ? "string, bytes, integer or None" :
|
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
|
||||||
path->allow_fd ? "string, bytes or integer" :
|
"integer or None" :
|
||||||
path->nullable ? "string, bytes or None" :
|
path->allow_fd ? "string, bytes, os.PathLike or integer" :
|
||||||
"string or bytes",
|
path->nullable ? "string, bytes, os.PathLike or None" :
|
||||||
|
"string, bytes or os.PathLike",
|
||||||
Py_TYPE(o)->tp_name);
|
Py_TYPE(o)->tp_name);
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
length = PyBytes_GET_SIZE(bytes);
|
length = PyBytes_GET_SIZE(bytes);
|
||||||
|
@ -951,7 +992,7 @@ path_converter(PyObject *o, void *p)
|
||||||
if (length > MAX_PATH-1) {
|
if (length > MAX_PATH-1) {
|
||||||
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
|
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
|
||||||
Py_DECREF(bytes);
|
Py_DECREF(bytes);
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -959,7 +1000,7 @@ path_converter(PyObject *o, void *p)
|
||||||
if ((size_t)length != strlen(narrow)) {
|
if ((size_t)length != strlen(narrow)) {
|
||||||
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
|
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
|
||||||
Py_DECREF(bytes);
|
Py_DECREF(bytes);
|
||||||
return 0;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
path->wide = NULL;
|
path->wide = NULL;
|
||||||
|
@ -969,12 +1010,15 @@ path_converter(PyObject *o, void *p)
|
||||||
path->fd = -1;
|
path->fd = -1;
|
||||||
if (bytes == o) {
|
if (bytes == o) {
|
||||||
Py_DECREF(bytes);
|
Py_DECREF(bytes);
|
||||||
return 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
path->cleanup = bytes;
|
path->cleanup = bytes;
|
||||||
return Py_CLEANUP_SUPPORTED;
|
ret = Py_CLEANUP_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
exit:
|
||||||
|
Py_XDECREF(to_cleanup);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -12329,6 +12373,8 @@ error:
|
||||||
PyObject *
|
PyObject *
|
||||||
PyOS_FSPath(PyObject *path)
|
PyOS_FSPath(PyObject *path)
|
||||||
{
|
{
|
||||||
|
/* For error message reasons, this function is manually inlined in
|
||||||
|
path_converter(). */
|
||||||
_Py_IDENTIFIER(__fspath__);
|
_Py_IDENTIFIER(__fspath__);
|
||||||
PyObject *func = NULL;
|
PyObject *func = NULL;
|
||||||
PyObject *path_repr = NULL;
|
PyObject *path_repr = NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue