From 0ed397a2980d15e47b927689516ada7e34de5c90 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 6 Apr 2024 17:36:01 +0200 Subject: [PATCH 01/16] Remove _get_sep() --- Lib/posixpath.py | 23 +++++++++-------------- Lib/test/test_posixpath.py | 2 ++ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 0e8bb5ab10d916..56cbad6bd0895c 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -38,12 +38,6 @@ "commonpath", "isjunction","isdevdrive"] -def _get_sep(path): - if isinstance(path, bytes): - return b'/' - else: - return '/' - # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. # On MS-DOS this may also turn slashes into backslashes; however, other # normalizations (such as optimizing '../' away) are not allowed @@ -60,7 +54,7 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" s = os.fspath(s) - sep = _get_sep(s) + sep = b'/' if isinstance(s, bytes) else '/' return s.startswith(sep) @@ -74,15 +68,15 @@ def join(a, *p): 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) + sep = b'/' if isinstance(a, bytes) else '/' path = a try: if not p: path[:0] + sep #23780: Ensure compatible data type even if p is null. for b in map(os.fspath, p): - if b.startswith(sep): + if b.startswith(sep) or not path: # startswith ensures no mixing path = b - elif not path or path.endswith(sep): + elif path.endswith(sep): path += b else: path += sep + b @@ -101,7 +95,7 @@ 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) + sep = b'/' if isinstance(p, bytes) else '/' i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] if head and head != sep*len(head): @@ -169,7 +163,7 @@ def splitroot(p): def basename(p): """Returns the final component of a pathname""" p = os.fspath(p) - sep = _get_sep(p) + sep = b'/' if isinstance(p, bytes) else '/' i = p.rfind(sep) + 1 return p[i:] @@ -179,7 +173,7 @@ def basename(p): def dirname(p): """Returns the directory component of a pathname""" p = os.fspath(p) - sep = _get_sep(p) + sep = b'/' if isinstance(p, bytes) else '/' i = p.rfind(sep) + 1 head = p[:i] if head and head != sep*len(head): @@ -232,11 +226,12 @@ def expanduser(path): path = os.fspath(path) if isinstance(path, bytes): tilde = b'~' + sep = b'/' else: tilde = '~' + sep = '/' if not path.startswith(tilde): return path - sep = _get_sep(path) i = path.find(sep, 1) if i < 0: i = len(path) diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 807f985f7f4df7..932344f1115909 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -56,6 +56,8 @@ def test_join(self): self.assertEqual(fn(b"/foo", b"bar", b"baz"), b"/foo/bar/baz") self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/") + self.assertEqual(fn("a", ""), "a/") + self.assertEqual(fn("a", "", ""), "a/") self.assertEqual(fn("a", "b"), "a/b") self.assertEqual(fn("a", "b/"), "a/b/") self.assertEqual(fn("a/", "b"), "a/b") From 0868dc6d48f3b0bc609259f9b60f0becb8624493 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 6 Apr 2024 18:21:06 +0200 Subject: [PATCH 02/16] Remove type check --- Lib/ntpath.py | 2 -- Lib/posixpath.py | 15 +++++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f9f6c78566e8ed..43763bd4aed082 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -108,8 +108,6 @@ def join(path, *paths): seps = '\\/' colon_seps = ':\\/' try: - if not paths: - path[:0] + sep #23780: Ensure compatible data type even if p is null. result_drive, result_root, result_path = splitroot(path) for p in map(os.fspath, paths): p_drive, p_root, p_path = splitroot(p) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 56cbad6bd0895c..0a10b3ae0b98f8 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -71,8 +71,6 @@ def join(a, *p): sep = b'/' if isinstance(a, bytes) else '/' path = a try: - if not p: - path[:0] + sep #23780: Ensure compatible data type even if p is null. for b in map(os.fspath, p): if b.startswith(sep) or not path: # startswith ensures no mixing path = b @@ -155,7 +153,7 @@ def splitroot(p): else: # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 - return empty, p[:2], p[2:] + return empty, sep + sep, p[2:] # Return the tail (basename) part of a path, same as split(path)[1]. @@ -225,11 +223,11 @@ def expanduser(path): do nothing.""" path = os.fspath(path) if isinstance(path, bytes): - tilde = b'~' sep = b'/' + tilde = b'~' else: - tilde = '~' sep = '/' + tilde = '~' if not path.startswith(tilde): return path i = path.find(sep, 1) @@ -271,11 +269,8 @@ def expanduser(path): return path if isinstance(path, bytes): userhome = os.fsencode(userhome) - root = b'/' - else: - root = '/' - userhome = userhome.rstrip(root) - return (userhome + path[i:]) or root + userhome = userhome.rstrip(sep) + return (userhome + path[i:]) or sep # Expand paths containing shell variable substitutions. From a431caa6efabaa2c0962e238ad3341576150d7c2 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 6 Apr 2024 18:37:00 +0200 Subject: [PATCH 03/16] Remove `os.fspath()` call on `curdir` --- Lib/ntpath.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 43763bd4aed082..4941f984b48384 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -782,7 +782,11 @@ def realpath(path, *, strict=False): def relpath(path, start=None): """Return a relative version of a path""" + path = os.fspath(path) + if not path: + raise ValueError("no path specified") + if isinstance(path, bytes): sep = b'\\' curdir = b'.' @@ -794,11 +798,9 @@ def relpath(path, start=None): if start is None: start = curdir + else: + start = os.fspath(start) - if not path: - raise ValueError("no path specified") - - start = os.fspath(start) try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path)) From 51d93eaba73cd6467ef8890910c27782f06334c4 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 6 Apr 2024 22:37:09 +0200 Subject: [PATCH 04/16] `str.join()` --- Lib/ntpath.py | 2 +- Lib/posixpath.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 4941f984b48384..56c226d3c648cf 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -822,7 +822,7 @@ def relpath(path, start=None): rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return join(*rel_list) + return sep.join(rel_list) except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 0a10b3ae0b98f8..15d272ea4e8117 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -184,6 +184,7 @@ def dirname(p): def ismount(path): """Test whether a path is a mount point""" + path = os.fspath(path) try: s1 = os.lstat(path) except (OSError, ValueError): @@ -194,7 +195,6 @@ def ismount(path): if stat.S_ISLNK(s1.st_mode): return False - path = os.fspath(path) if isinstance(path, bytes): parent = join(path, b'..') else: @@ -519,7 +519,7 @@ def relpath(path, start=None): rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return join(*rel_list) + return sep.join(rel_list) except (TypeError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise From a64e87b6c07eecda6ac9f3af6082755dd4f9b7b9 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 08:00:24 +0200 Subject: [PATCH 05/16] simplify split --- Lib/ntpath.py | 4 ++-- Lib/posixpath.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 56c226d3c648cf..03210587cb820e 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -810,8 +810,8 @@ def relpath(path, start=None): raise ValueError("path is on mount %r, start on mount %r" % ( path_drive, start_drive)) - start_list = [x for x in start_rest.split(sep) if x] - path_list = [x for x in path_rest.split(sep) if x] + start_list = start_rest.split(sep) if start_rest else [] + path_list = path_rest.split(sep) if path_rest else [] # Work out how much of the filepath is shared by start and path. i = 0 for e1, e2 in zip(start_list, path_list): diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 15d272ea4e8117..a1d3c040204b3c 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -511,8 +511,10 @@ def relpath(path, start=None): start = os.fspath(start) try: - start_list = [x for x in abspath(start).split(sep) if x] - path_list = [x for x in abspath(path).split(sep) if x] + start_rest = abspath(start).lstrip(sep) + path_rest = abspath(path).lstrip(sep) + start_list = start_rest.split(sep) if start_rest else [] + path_list = path_rest.split(sep) if path_rest else [] # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) From 906fecb8be1471b201d481ea9bae1c372464223b Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 14:08:49 +0200 Subject: [PATCH 06/16] don't normalise cwd --- Lib/ntpath.py | 6 ++++-- Lib/posixpath.py | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 03210587cb820e..cae46646211f9e 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -791,10 +791,12 @@ def relpath(path, start=None): sep = b'\\' curdir = b'.' pardir = b'..' + getcwd = os.getcwdb else: sep = '\\' curdir = '.' pardir = '..' + getcwd = os.getcwd if start is None: start = curdir @@ -802,8 +804,8 @@ def relpath(path, start=None): start = os.fspath(start) try: - start_abs = abspath(normpath(start)) - path_abs = abspath(normpath(path)) + start_abs = getcwd() if not start or start == curdir else abspath(start) + path_abs = abspath(path) start_drive, _, start_rest = splitroot(start_abs) path_drive, _, path_rest = splitroot(path_abs) if normcase(start_drive) != normcase(path_drive): diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 62b54f461168cd..59718ed110f864 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -348,7 +348,7 @@ def normpath(path): sep = '/' dot = '.' dotdot = '..' - if not path: + if not path or path == dot: return dot _, initial_slashes, path = splitroot(path) comps = path.split(sep) @@ -500,10 +500,12 @@ def relpath(path, start=None): curdir = b'.' sep = b'/' pardir = b'..' + getcwd = os.getcwdb else: curdir = '.' sep = '/' pardir = '..' + getcwd = os.getcwd if start is None: start = curdir @@ -511,10 +513,12 @@ def relpath(path, start=None): start = os.fspath(start) try: - start_rest = abspath(start).lstrip(sep) - path_rest = abspath(path).lstrip(sep) - start_list = start_rest.split(sep) if start_rest else [] - path_list = path_rest.split(sep) if path_rest else [] + start_abs = getcwd() if not start or start == curdir else abspath(start) + path_abs = abspath(path) + start_tail = start_abs.lstrip(sep) + path_tail = path_abs.lstrip(sep) + start_list = start_tail.split(sep) if start_tail else [] + path_list = path_tail.split(sep) if path_tail else [] # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) From bd30c3c4436131064b6c1ff16abff06a44c52faa Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 14:22:49 +0200 Subject: [PATCH 07/16] clean up mix check --- Lib/posixpath.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 59718ed110f864..250b2d22d6ebef 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -554,10 +554,8 @@ def commonpath(paths): try: split_paths = [path.split(sep) for path in paths] - try: - isabs, = set(p[:1] == sep for p in paths) - except ValueError: - raise ValueError("Can't mix absolute and relative paths") from None + if len({p.startswith(sep) for p in paths}) != 1: + raise ValueError("Can't mix absolute and relative paths") split_paths = [[c for c in s if c and c != curdir] for s in split_paths] s1 = min(split_paths) @@ -568,7 +566,7 @@ def commonpath(paths): common = s1[:i] break - prefix = sep if isabs else sep[:0] + prefix = sep if paths[0].startswith(sep) else sep[:0] return prefix + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) From 07b1bd3b24986841599f82d34afa7d53367de859 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 14:44:47 +0200 Subject: [PATCH 08/16] remove `_get_bothseps` --- Lib/ntpath.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index cae46646211f9e..719354709091b1 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -32,12 +32,6 @@ "samefile", "sameopenfile", "samestat", "commonpath", "isjunction", "isdevdrive"] -def _get_bothseps(path): - if isinstance(path, bytes): - return b'\\/' - else: - return '\\/' - # Normalize the case of a pathname and map slashes to backslashes. # Other normalizations (such as optimizing '../' away) are not done # (this is done by normpath). @@ -230,7 +224,7 @@ def split(p): Return tuple (head, tail) where tail is everything after the final slash. Either part may be empty.""" p = os.fspath(p) - seps = _get_bothseps(p) + seps = b'\\/' if isinstance(p, bytes) else '\\/' d, r, p = splitroot(p) # set i to index beyond p's last slash i = len(p) @@ -301,7 +295,7 @@ 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) + seps = b'\\/' if isinstance(path, bytes) else '\\/' path = abspath(path) drive, root, rest = splitroot(path) if drive and drive[0] in seps: @@ -366,13 +360,15 @@ def expanduser(path): If user or $HOME is unknown, do nothing.""" path = os.fspath(path) if isinstance(path, bytes): + seps = b'\\/' tilde = b'~' else: + seps = '\\/' tilde = '~' if not path.startswith(tilde): return path i, n = 1, len(path) - while i < n and path[i] not in _get_bothseps(path): + while i < n and path[i] not in seps: i += 1 if 'USERPROFILE' in os.environ: @@ -433,6 +429,7 @@ def expandvars(path): brace = b'{' rbrace = b'}' dollar = b'$' + empty = b'' environ = getattr(os, 'environb', None) else: if '$' not in path and '%' not in path: @@ -444,8 +441,9 @@ def expandvars(path): brace = '{' rbrace = '}' dollar = '$' + empty = '' environ = os.environ - res = path[:0] + res = empty index = 0 pathlen = len(path) while index < pathlen: @@ -504,7 +502,7 @@ def expandvars(path): value = dollar + brace + var + rbrace res += value else: - var = path[:0] + var = empty index += 1 c = path[index:index + 1] while c and c in varchars: From c2d73f6f9f2ebd78a6025c6dda9a086162978d22 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 16:36:16 +0200 Subject: [PATCH 09/16] speedup `realpath()` for relative paths --- Lib/ntpath.py | 10 +++++++--- Lib/posixpath.py | 14 ++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 719354709091b1..c1a79de4d86ecb 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -587,9 +587,13 @@ def _abspath_fallback(path): path = os.fspath(path) if not isabs(path): if isinstance(path, bytes): + curdir = b'.' cwd = os.getcwdb() else: + curdir = '.' cwd = os.getcwd() + if not path or path == curdir: + return cwd path = join(cwd, path) return normpath(path) @@ -716,7 +720,7 @@ def realpath(path, *, strict=False): prefix = b'\\\\?\\' unc_prefix = b'\\\\?\\UNC\\' new_unc_prefix = b'\\\\' - cwd = os.getcwdb() + getcwd = os.getcwdb # bpo-38081: Special case for realpath(b'nul') devnull = b'nul' if normcase(path) == devnull: @@ -725,14 +729,14 @@ def realpath(path, *, strict=False): prefix = '\\\\?\\' unc_prefix = '\\\\?\\UNC\\' new_unc_prefix = '\\\\' - cwd = os.getcwd() + getcwd = os.getcwd # bpo-38081: Special case for realpath('nul') devnull = 'nul' if normcase(path) == devnull: return '\\\\.\\NUL' had_prefix = path.startswith(prefix) if not had_prefix and not isabs(path): - path = join(cwd, path) + path = join(getcwd(), path) try: path = _getfinalpathname(path) initial_winerror = 0 diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 250b2d22d6ebef..b3ca2acae4c4fc 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -378,11 +378,17 @@ def abspath(path): """Return an absolute path.""" path = os.fspath(path) if isinstance(path, bytes): - if not path.startswith(b'/'): - path = join(os.getcwdb(), path) + sep = b'/' + curdir = b'.' + getcwd = os.getcwdb else: - if not path.startswith('/'): - path = join(os.getcwd(), path) + sep = '/' + curdir = '.' + getcwd = os.getcwd + if not path.startswith(sep): + if not path or path == curdir: + return getcwd() + path = join(getcwd(), path) return normpath(path) From 62c7b972dd9cbbefc1f311e803689108c8da722f Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 16:56:20 +0200 Subject: [PATCH 10/16] save `normcase(path)` in variable --- Lib/ntpath.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index c1a79de4d86ecb..40da83cf18bea1 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -563,10 +563,8 @@ def normpath(path): i += 1 else: i += 1 - # If the path is now empty, substitute '.' - if not prefix and not comps: - comps.append(curdir) - return prefix + sep.join(comps) + path = prefix + sep.join(comps) + return path or curdir else: def normpath(path): @@ -636,8 +634,9 @@ def _readlink_deep(path): allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 4390, 4392, 4393 seen = set() - while normcase(path) not in seen: - seen.add(normcase(path)) + normp = normcase(path) + while normp not in seen: + seen.add(normp) try: old_path = path path = _nt_readlink(path) @@ -651,6 +650,7 @@ def _readlink_deep(path): path = old_path break path = normpath(join(dirname(old_path), path)) + normp = normcase(path) except OSError as ex: if ex.winerror in allowed_winerror: break From 6dfe0886b2fda79ffe6da5516d7b337462aa6ab0 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 19:05:03 +0200 Subject: [PATCH 11/16] Remove newline --- Lib/ntpath.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 40da83cf18bea1..f9885f69267391 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -784,7 +784,6 @@ def realpath(path, *, strict=False): def relpath(path, start=None): """Return a relative version of a path""" - path = os.fspath(path) if not path: raise ValueError("no path specified") From d968aa010ea4a59349e4c7e50ca71ece92c3e7c1 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 19:16:42 +0200 Subject: [PATCH 12/16] remove empty slice --- Lib/posixpath.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index b3ca2acae4c4fc..7ac5e2a26aab37 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -553,9 +553,11 @@ def commonpath(paths): if isinstance(paths[0], bytes): sep = b'/' curdir = b'.' + empty = b'' else: sep = '/' curdir = '.' + empty = '' try: split_paths = [path.split(sep) for path in paths] @@ -572,7 +574,7 @@ def commonpath(paths): common = s1[:i] break - prefix = sep if paths[0].startswith(sep) else sep[:0] + prefix = sep if paths[0].startswith(sep) else empty return prefix + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) From da7e437d64aa1679dbedd7f8242491f45c711053 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sun, 7 Apr 2024 20:46:04 +0200 Subject: [PATCH 13/16] Revert `relpath()` --- Lib/ntpath.py | 21 +++++++++------------ Lib/posixpath.py | 12 +++--------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f9885f69267391..70bcb3d6e414eb 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -785,36 +785,33 @@ def realpath(path, *, strict=False): def relpath(path, start=None): """Return a relative version of a path""" path = os.fspath(path) - if not path: - raise ValueError("no path specified") - if isinstance(path, bytes): sep = b'\\' curdir = b'.' pardir = b'..' - getcwd = os.getcwdb else: sep = '\\' curdir = '.' pardir = '..' - getcwd = os.getcwd if start is None: start = curdir - else: - start = os.fspath(start) + if not path: + raise ValueError("no path specified") + + start = os.fspath(start) try: - start_abs = getcwd() if not start or start == curdir else abspath(start) - path_abs = abspath(path) + start_abs = abspath(normpath(start)) + path_abs = abspath(normpath(path)) start_drive, _, start_rest = splitroot(start_abs) path_drive, _, path_rest = splitroot(path_abs) if normcase(start_drive) != normcase(path_drive): raise ValueError("path is on mount %r, start on mount %r" % ( path_drive, start_drive)) - start_list = start_rest.split(sep) if start_rest else [] - path_list = path_rest.split(sep) if path_rest else [] + start_list = [x for x in start_rest.split(sep) if x] + path_list = [x for x in path_rest.split(sep) if x] # Work out how much of the filepath is shared by start and path. i = 0 for e1, e2 in zip(start_list, path_list): @@ -825,7 +822,7 @@ def relpath(path, start=None): rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return sep.join(rel_list) + return join(*rel_list) except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 7ac5e2a26aab37..29dc8e04a02471 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -506,12 +506,10 @@ def relpath(path, start=None): curdir = b'.' sep = b'/' pardir = b'..' - getcwd = os.getcwdb else: curdir = '.' sep = '/' pardir = '..' - getcwd = os.getcwd if start is None: start = curdir @@ -519,19 +517,15 @@ def relpath(path, start=None): start = os.fspath(start) try: - start_abs = getcwd() if not start or start == curdir else abspath(start) - path_abs = abspath(path) - start_tail = start_abs.lstrip(sep) - path_tail = path_abs.lstrip(sep) - start_list = start_tail.split(sep) if start_tail else [] - path_list = path_tail.split(sep) if path_tail else [] + start_list = [x for x in abspath(start).split(sep) if x] + path_list = [x for x in abspath(path).split(sep) if x] # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir - return sep.join(rel_list) + return join(*rel_list) except (TypeError, AttributeError, BytesWarning, DeprecationWarning): genericpath._check_arg_types('relpath', path, start) raise From 11fcb2fc928b0dd19848b3370764326892c0c5e1 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 7 Apr 2024 19:03:54 +0000 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-04-07-19-03-53.gh-issue-117609.J-C6J2.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-07-19-03-53.gh-issue-117609.J-C6J2.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-07-19-03-53.gh-issue-117609.J-C6J2.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-07-19-03-53.gh-issue-117609.J-C6J2.rst new file mode 100644 index 00000000000000..7932a390ac150a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-07-19-03-53.gh-issue-117609.J-C6J2.rst @@ -0,0 +1 @@ +Speedup some functions in :mod:`os.path`. From 5f7025f5c68a0c19f0e5015c636277b53e1826ed Mon Sep 17 00:00:00 2001 From: nineteendo Date: Mon, 8 Apr 2024 10:49:33 +0200 Subject: [PATCH 15/16] Fix tests --- Lib/ntpath.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 70bcb3d6e414eb..f753ffaa71d0bd 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -585,13 +585,9 @@ def _abspath_fallback(path): path = os.fspath(path) if not isabs(path): if isinstance(path, bytes): - curdir = b'.' cwd = os.getcwdb() else: - curdir = '.' cwd = os.getcwd() - if not path or path == curdir: - return cwd path = join(cwd, path) return normpath(path) @@ -650,7 +646,7 @@ def _readlink_deep(path): path = old_path break path = normpath(join(dirname(old_path), path)) - normp = normcase(path) + normp = normcase(path) except OSError as ex: if ex.winerror in allowed_winerror: break From d9680f011a70d1721f90b4122e17b19d3823bd1a Mon Sep 17 00:00:00 2001 From: nineteendo Date: Mon, 8 Apr 2024 11:43:34 +0200 Subject: [PATCH 16/16] Revert empty slices --- Lib/ntpath.py | 6 ++---- Lib/posixpath.py | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f753ffaa71d0bd..a8fa245857a7e3 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -429,7 +429,6 @@ def expandvars(path): brace = b'{' rbrace = b'}' dollar = b'$' - empty = b'' environ = getattr(os, 'environb', None) else: if '$' not in path and '%' not in path: @@ -441,9 +440,8 @@ def expandvars(path): brace = '{' rbrace = '}' dollar = '$' - empty = '' environ = os.environ - res = empty + res = path[:0] index = 0 pathlen = len(path) while index < pathlen: @@ -502,7 +500,7 @@ def expandvars(path): value = dollar + brace + var + rbrace res += value else: - var = empty + var = path[:0] index += 1 c = path[index:index + 1] while c and c in varchars: diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 29dc8e04a02471..07073f26c94f09 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -547,11 +547,9 @@ def commonpath(paths): if isinstance(paths[0], bytes): sep = b'/' curdir = b'.' - empty = b'' else: sep = '/' curdir = '.' - empty = '' try: split_paths = [path.split(sep) for path in paths] @@ -568,7 +566,7 @@ def commonpath(paths): common = s1[:i] break - prefix = sep if paths[0].startswith(sep) else empty + prefix = sep if paths[0].startswith(sep) else sep[:0] return prefix + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths)