gh-117349: Micro-optimize a few os.path functions (#117350)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Barney Gale <barney.gale@gmail.com>
Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
This commit is contained in:
Nice Zombies 2024-04-02 22:32:35 +02:00 committed by GitHub
parent 8eda146e87
commit cae4cdd07d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 35 additions and 39 deletions

View file

@ -102,11 +102,11 @@ def join(path, *paths):
if isinstance(path, bytes): if isinstance(path, bytes):
sep = b'\\' sep = b'\\'
seps = b'\\/' seps = b'\\/'
colon = b':' colon_seps = b':\\/'
else: else:
sep = '\\' sep = '\\'
seps = '\\/' seps = '\\/'
colon = ':' colon_seps = ':\\/'
try: try:
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.
@ -135,7 +135,7 @@ def join(path, *paths):
result_path = result_path + p_path result_path = result_path + p_path
## add separator between UNC and non-absolute path ## add separator between UNC and non-absolute path
if (result_path and not result_root and if (result_path and not result_root and
result_drive and result_drive[-1:] not in colon + seps): result_drive and result_drive[-1] not in colon_seps):
return result_drive + sep + result_path return result_drive + sep + result_path
return result_drive + result_root + result_path return result_drive + result_root + result_path
except (TypeError, AttributeError, BytesWarning): except (TypeError, AttributeError, BytesWarning):
@ -279,7 +279,7 @@ if hasattr(os.stat_result, 'st_reparse_tag'):
st = os.lstat(path) st = os.lstat(path)
except (OSError, ValueError, AttributeError): except (OSError, ValueError, AttributeError):
return False return False
return bool(st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT) return st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT
else: else:
# Use genericpath.isjunction as imported above # Use genericpath.isjunction as imported above
pass pass
@ -340,8 +340,8 @@ def isreserved(path):
def _isreservedname(name): def _isreservedname(name):
"""Return true if the filename is reserved by the system.""" """Return true if the filename is reserved by the system."""
# Trailing dots and spaces are reserved. # Trailing dots and spaces are reserved.
if name.endswith(('.', ' ')) and name not in ('.', '..'): if name[-1:] in ('.', ' '):
return True return name not in ('.', '..')
# Wildcards, separators, colon, and pipe (*?"<>/\:|) are reserved. # Wildcards, separators, colon, and pipe (*?"<>/\:|) are reserved.
# ASCII control characters (0-31) are reserved. # ASCII control characters (0-31) are reserved.
# Colon is reserved for file streams (e.g. "name:stream[:type]"). # Colon is reserved for file streams (e.g. "name:stream[:type]").
@ -350,9 +350,7 @@ def _isreservedname(name):
# DOS device names are reserved (e.g. "nul" or "nul .txt"). The rules # DOS device names are reserved (e.g. "nul" or "nul .txt"). The rules
# are complex and vary across Windows versions. On the side of # are complex and vary across Windows versions. On the side of
# caution, return True for names that may not be reserved. # caution, return True for names that may not be reserved.
if name.partition('.')[0].rstrip(' ').upper() in _reserved_names: return name.partition('.')[0].rstrip(' ').upper() in _reserved_names
return True
return False
# Expand paths beginning with '~' or '~user'. # Expand paths beginning with '~' or '~user'.
@ -381,13 +379,10 @@ def expanduser(path):
if 'USERPROFILE' in os.environ: if 'USERPROFILE' in os.environ:
userhome = os.environ['USERPROFILE'] userhome = os.environ['USERPROFILE']
elif not 'HOMEPATH' in os.environ: elif 'HOMEPATH' not in os.environ:
return path return path
else: else:
try: drive = os.environ.get('HOMEDRIVE', '')
drive = os.environ['HOMEDRIVE']
except KeyError:
drive = ''
userhome = join(drive, os.environ['HOMEPATH']) userhome = join(drive, os.environ['HOMEPATH'])
if i != 1: #~user if i != 1: #~user
@ -727,7 +722,8 @@ else:
new_unc_prefix = b'\\\\' new_unc_prefix = b'\\\\'
cwd = os.getcwdb() cwd = os.getcwdb()
# bpo-38081: Special case for realpath(b'nul') # bpo-38081: Special case for realpath(b'nul')
if normcase(path) == normcase(os.fsencode(devnull)): devnull = b'nul'
if normcase(path) == devnull:
return b'\\\\.\\NUL' return b'\\\\.\\NUL'
else: else:
prefix = '\\\\?\\' prefix = '\\\\?\\'
@ -735,7 +731,8 @@ else:
new_unc_prefix = '\\\\' new_unc_prefix = '\\\\'
cwd = os.getcwd() cwd = os.getcwd()
# bpo-38081: Special case for realpath('nul') # bpo-38081: Special case for realpath('nul')
if normcase(path) == normcase(devnull): devnull = 'nul'
if normcase(path) == devnull:
return '\\\\.\\NUL' return '\\\\.\\NUL'
had_prefix = path.startswith(prefix) had_prefix = path.startswith(prefix)
if not had_prefix and not isabs(path): if not had_prefix and not isabs(path):

View file

@ -213,15 +213,8 @@ def ismount(path):
except (OSError, ValueError): except (OSError, ValueError):
return False return False
dev1 = s1.st_dev # path/.. on a different device as path or the same i-node as path
dev2 = s2.st_dev return s1.st_dev != s2.st_dev or s1.st_ino == s2.st_ino
if dev1 != dev2:
return True # path/.. on a different device as path
ino1 = s1.st_ino
ino2 = s2.st_ino
if ino1 == ino2:
return True # path/.. is the same i-node as path
return False
# Expand paths beginning with '~' or '~user'. # Expand paths beginning with '~' or '~user'.
@ -270,7 +263,7 @@ def expanduser(path):
return path return path
name = path[1:i] name = path[1:i]
if isinstance(name, bytes): if isinstance(name, bytes):
name = str(name, 'ASCII') name = name.decode('ascii')
try: try:
pwent = pwd.getpwnam(name) pwent = pwd.getpwnam(name)
except KeyError: except KeyError:
@ -359,21 +352,19 @@ except ImportError:
path = os.fspath(path) path = os.fspath(path)
if isinstance(path, bytes): if isinstance(path, bytes):
sep = b'/' sep = b'/'
empty = b''
dot = b'.' dot = b'.'
dotdot = b'..' dotdot = b'..'
else: else:
sep = '/' sep = '/'
empty = ''
dot = '.' dot = '.'
dotdot = '..' dotdot = '..'
if path == empty: if not path:
return dot return dot
_, initial_slashes, path = splitroot(path) _, initial_slashes, path = splitroot(path)
comps = path.split(sep) comps = path.split(sep)
new_comps = [] new_comps = []
for comp in comps: for comp in comps:
if comp in (empty, dot): if not comp or comp == dot:
continue continue
if (comp != dotdot or (not initial_slashes and not new_comps) or if (comp != dotdot or (not initial_slashes and not new_comps) or
(new_comps and new_comps[-1] == dotdot)): (new_comps and new_comps[-1] == dotdot)):
@ -396,12 +387,12 @@ else:
def abspath(path): def abspath(path):
"""Return an absolute path.""" """Return an absolute path."""
path = os.fspath(path) path = os.fspath(path)
if not isabs(path): if isinstance(path, bytes):
if isinstance(path, bytes): if not path.startswith(b'/'):
cwd = os.getcwdb() path = join(os.getcwdb(), path)
else: else:
cwd = os.getcwd() if not path.startswith('/'):
path = join(cwd, path) path = join(os.getcwd(), path)
return normpath(path) return normpath(path)
@ -417,6 +408,7 @@ symbolic links encountered in the path."""
# Join two paths, normalizing and eliminating any symbolic links # Join two paths, normalizing and eliminating any symbolic links
# encountered in the second path. # encountered in the second path.
# Two leading slashes are replaced by a single slash.
def _joinrealpath(path, rest, strict, seen): def _joinrealpath(path, rest, strict, seen):
if isinstance(path, bytes): if isinstance(path, bytes):
sep = b'/' sep = b'/'
@ -427,7 +419,7 @@ def _joinrealpath(path, rest, strict, seen):
curdir = '.' curdir = '.'
pardir = '..' pardir = '..'
if isabs(rest): if rest.startswith(sep):
rest = rest[1:] rest = rest[1:]
path = sep path = sep
@ -439,10 +431,15 @@ def _joinrealpath(path, rest, strict, seen):
if name == pardir: if name == pardir:
# parent dir # parent dir
if path: if path:
path, name = split(path) parent, name = split(path)
if name == pardir: if name == pardir:
path = join(path, pardir, pardir) # ../..
path = join(path, pardir)
else:
# foo/bar/.. -> foo
path = parent
else: else:
# ..
path = pardir path = pardir
continue continue
newpath = join(path, name) newpath = join(path, name)

View file

@ -191,6 +191,7 @@ Finn Bock
Paul Boddie Paul Boddie
Matthew Boedicker Matthew Boedicker
Robin Boerdijk Robin Boerdijk
Wannes Boeykens
Andra Bogildea Andra Bogildea
Matt Bogosian Matt Bogosian
Nikolay Bogoychev Nikolay Bogoychev

View file

@ -0,0 +1 @@
Optimise several functions in :mod:`os.path`.