File CVE-2025-6075-expandvars-perf-degrad.patch of Package python3.42038
From 8b8e68d3dc95f454f58fdd8aac10848facb1491d Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 31 Oct 2025 15:49:51 +0200
Subject: [PATCH 1/2] [3.9] gh-136065: Fix quadratic complexity in
os.path.expandvars() (GH-134952) (cherry picked from commit
f029e8db626ddc6e3a3beea4eff511a71aaceb5c)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
---
Lib/ntpath.py | 404 +++---
Lib/posixpath.py | 276 ++--
Lib/test/test_genericpath.py | 18
Lib/test/test_ntpath.py | 653 +++++-----
Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst | 1
5 files changed, 771 insertions(+), 581 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
Index: Python-3.6.15/Lib/ntpath.py
===================================================================
--- Python-3.6.15.orig/Lib/ntpath.py 2025-12-15 22:44:15.347462599 +0100
+++ Python-3.6.15/Lib/ntpath.py 2025-12-15 22:54:52.589626377 +0100
@@ -8,14 +8,14 @@
# strings representing various path-related bits and pieces
# These are primarily for export; internally, they are hardcoded.
# Should be set before imports for resolving cyclic dependency.
-curdir = '.'
-pardir = '..'
-extsep = '.'
-sep = '\\'
-pathsep = ';'
-altsep = '/'
-defpath = '.;C:\\bin'
-devnull = 'nul'
+curdir = "."
+pardir = ".."
+extsep = "."
+sep = "\\"
+pathsep = ";"
+altsep = "/"
+defpath = ".;C:\\bin"
+devnull = "nul"
import os
import sys
@@ -23,24 +23,61 @@
import genericpath
from genericpath import *
-__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
- "basename","dirname","commonprefix","getsize","getmtime",
- "getatime","getctime", "islink","exists","lexists","isdir","isfile",
- "ismount", "expanduser","expandvars","normpath","abspath",
- "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
- "extsep","devnull","realpath","supports_unicode_filenames","relpath",
- "samefile", "sameopenfile", "samestat", "commonpath"]
+__all__ = [
+ "normcase",
+ "isabs",
+ "join",
+ "splitdrive",
+ "split",
+ "splitext",
+ "basename",
+ "dirname",
+ "commonprefix",
+ "getsize",
+ "getmtime",
+ "getatime",
+ "getctime",
+ "islink",
+ "exists",
+ "lexists",
+ "isdir",
+ "isfile",
+ "ismount",
+ "expanduser",
+ "expandvars",
+ "normpath",
+ "abspath",
+ "splitunc",
+ "curdir",
+ "pardir",
+ "sep",
+ "pathsep",
+ "defpath",
+ "altsep",
+ "extsep",
+ "devnull",
+ "realpath",
+ "supports_unicode_filenames",
+ "relpath",
+ "samefile",
+ "sameopenfile",
+ "samestat",
+ "commonpath",
+]
+
def _get_bothseps(path):
if isinstance(path, bytes):
- return b'\\/'
+ return b"\\/"
else:
- return '\\/'
+ 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).
+
def normcase(s):
"""Normalize case of pathname.
@@ -48,13 +85,15 @@
s = os.fspath(s)
try:
if isinstance(s, bytes):
- return s.replace(b'/', b'\\').lower()
+ return s.replace(b"/", b"\\").lower()
else:
- return s.replace('/', '\\').lower()
+ return s.replace("/", "\\").lower()
except (TypeError, AttributeError):
if not isinstance(s, (bytes, str)):
- raise TypeError("normcase() argument must be str or bytes, "
- "not %r" % s.__class__.__name__) from None
+ raise TypeError(
+ "normcase() argument must be str or bytes, "
+ "not %r" % s.__class__.__name__
+ ) from None
raise
@@ -64,6 +103,7 @@
# volume), or if a pathname after the volume-letter-and-colon or UNC-resource
# starts with a slash or backslash.
+
def isabs(s):
"""Test whether a path is absolute"""
s = os.fspath(s)
@@ -75,16 +115,16 @@
def join(path, *paths):
path = os.fspath(path)
if isinstance(path, bytes):
- sep = b'\\'
- seps = b'\\/'
- colon = b':'
+ sep = b"\\"
+ seps = b"\\/"
+ colon = b":"
else:
- sep = '\\'
- seps = '\\/'
- colon = ':'
+ sep = "\\"
+ seps = "\\/"
+ colon = ":"
try:
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)
for p in map(os.fspath, paths):
p_drive, p_path = splitdrive(p)
@@ -107,12 +147,16 @@
result_path = result_path + sep
result_path = result_path + p_path
## add separator between UNC and non-absolute path
- if (result_path and result_path[0] not in seps and
- result_drive and result_drive[-1:] != colon):
+ if (
+ result_path
+ and result_path[0] not in seps
+ and result_drive
+ and result_drive[-1:] != colon
+ ):
return result_drive + sep + result_path
return result_drive + result_path
except (TypeError, AttributeError, BytesWarning):
- genericpath._check_arg_types('join', path, *paths)
+ genericpath._check_arg_types("join", path, *paths)
raise
@@ -141,15 +185,15 @@
p = os.fspath(p)
if len(p) >= 2:
if isinstance(p, bytes):
- sep = b'\\'
- altsep = b'/'
- colon = b':'
+ sep = b"\\"
+ altsep = b"/"
+ colon = b":"
else:
- sep = '\\'
- altsep = '/'
- colon = ':'
+ sep = "\\"
+ altsep = "/"
+ colon = ":"
normp = p.replace(altsep, sep)
- if (normp[0:2] == sep*2) and (normp[2:3] != sep):
+ if (normp[0:2] == sep * 2) and (normp[2:3] != sep):
# is a UNC path:
# vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
# \\machine\mountpoint\directory\etc\...
@@ -183,11 +227,15 @@
Paths containing drive letters never have a UNC part.
"""
import warnings
- warnings.warn("ntpath.splitunc is deprecated, use ntpath.splitdrive instead",
- DeprecationWarning, 2)
+
+ warnings.warn(
+ "ntpath.splitunc is deprecated, use ntpath.splitdrive instead",
+ DeprecationWarning,
+ 2,
+ )
drive, path = splitdrive(p)
if len(drive) == 2:
- # Drive letter present
+ # Drive letter present
return p[:0], p
return drive, path
@@ -197,6 +245,7 @@
# join(head, tail) == p holds.
# The resulting head won't end in '/' unless it is the root.
+
def split(p):
"""Split a pathname.
@@ -207,7 +256,7 @@
d, p = splitdrive(p)
# set i to index beyond p's last slash
i = len(p)
- while i and p[i-1] not in seps:
+ while i and p[i - 1] not in seps:
i -= 1
head, tail = p[:i], p[i:] # now tail has no slashes
# remove trailing slashes from head, unless it's all slashes
@@ -220,17 +269,21 @@
# pathname component; the root is everything before that.
# It is always true that root + ext == p.
+
def splitext(p):
p = os.fspath(p)
if isinstance(p, bytes):
- return genericpath._splitext(p, b'\\', b'/', b'.')
+ return genericpath._splitext(p, b"\\", b"/", b".")
else:
- return genericpath._splitext(p, '\\', '/', '.')
+ return genericpath._splitext(p, "\\", "/", ".")
+
+
splitext.__doc__ = genericpath._splitext.__doc__
# Return the tail (basename) part of a path.
+
def basename(p):
"""Returns the final component of a pathname"""
return split(p)[1]
@@ -238,13 +291,16 @@
# Return the head (dirname) part of a path.
+
def dirname(p):
"""Returns the directory component of a pathname"""
return split(p)[0]
+
# Is a path a symbolic link?
# This will always return false on systems where os.lstat doesn't exist.
+
def islink(path):
"""Test whether a path is a symbolic link.
This will always return false for Windows prior to 6.0.
@@ -255,8 +311,10 @@
return False
return stat.S_ISLNK(st.st_mode)
+
# Being true for dangling symbolic links is also useful.
+
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
@@ -265,6 +323,7 @@
return False
return True
+
# Is a path a mount point?
# Any drive letter root (eg c:\)
# Any share UNC (eg \\server\share)
@@ -279,6 +338,8 @@
from nt import _getvolumepathname
except ImportError:
_getvolumepathname = None
+
+
def ismount(path):
"""Test whether a path is a mount point (a drive root, the root of a
share, or a mounted volume)"""
@@ -306,38 +367,39 @@
# (A function should also be defined to do full *sh-style environment
# variable expansion.)
+
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'~'
+ tilde = b"~"
else:
- tilde = '~'
+ tilde = "~"
if not path.startswith(tilde):
return path
i, n = 1, len(path)
while i < n and path[i] not in _get_bothseps(path):
i += 1
- if 'HOME' in os.environ:
- userhome = os.environ['HOME']
- elif 'USERPROFILE' in os.environ:
- userhome = os.environ['USERPROFILE']
- elif not 'HOMEPATH' in os.environ:
+ if "HOME" in os.environ:
+ userhome = os.environ["HOME"]
+ elif "USERPROFILE" in os.environ:
+ userhome = os.environ["USERPROFILE"]
+ elif not "HOMEPATH" in os.environ:
return path
else:
try:
- drive = os.environ['HOMEDRIVE']
+ drive = os.environ["HOMEDRIVE"]
except KeyError:
- drive = ''
- userhome = join(drive, os.environ['HOMEPATH'])
+ drive = ""
+ userhome = join(drive, os.environ["HOMEPATH"])
if isinstance(path, bytes):
userhome = os.fsencode(userhome)
- if i != 1: #~user
+ if i != 1: # ~user
userhome = join(dirname(userhome), path[1:i])
return userhome + path[i:]
@@ -356,134 +418,100 @@
# XXX With COMMAND.COM you can use any characters in a variable name,
# XXX except '^|<>='.
+_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
+_varsub = None
+_varsubb = None
+
+
def expandvars(path):
"""Expand shell variables of the forms $var, ${var} and %var%.
Unknown variables are left unchanged."""
+ global _varsub, _varsubb
path = os.fspath(path)
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
import string
- varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
- quote = b'\''
- percent = b'%'
- brace = b'{'
- rbrace = b'}'
- dollar = b'$'
- environ = getattr(os, 'environb', None)
+
+ if not _varsubb:
+ import re
+
+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
+ sub = _varsubb
+ percent = b"%"
+ brace = b"{"
+ rbrace = b"}"
+ dollar = b"$"
+ environ = getattr(os, "environb", None)
else:
- if '$' not in path and '%' not in path:
+ if "$" not in path and "%" not in path:
return path
import string
- varchars = string.ascii_letters + string.digits + '_-'
- quote = '\''
- percent = '%'
- brace = '{'
- rbrace = '}'
- dollar = '$'
+
+ if not _varsub:
+ import re
+
+ _varsub = re.compile(_varpattern, re.ASCII).sub
+ sub = _varsub
+ varchars = string.ascii_letters + string.digits + "_-"
+ quote = "'"
+ percent = "%"
+ brace = "{"
+ rbrace = "}"
+ dollar = "$"
environ = os.environ
- res = path[:0]
- index = 0
- pathlen = len(path)
- while index < pathlen:
- c = path[index:index+1]
- if c == quote: # no expansion within single quotes
- path = path[index + 1:]
- pathlen = len(path)
- try:
- index = path.index(c)
- res += c + path[:index + 1]
- except ValueError:
- res += c + path
- index = pathlen - 1
- elif c == percent: # variable or '%'
- if path[index + 1:index + 2] == percent:
- res += c
- index += 1
- else:
- path = path[index+1:]
- pathlen = len(path)
- try:
- index = path.index(percent)
- except ValueError:
- res += percent + path
- index = pathlen - 1
- else:
- var = path[:index]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = percent + var + percent
- res += value
- elif c == dollar: # variable or '$$'
- if path[index + 1:index + 2] == dollar:
- res += c
- index += 1
- elif path[index + 1:index + 2] == brace:
- path = path[index+2:]
- pathlen = len(path)
- try:
- index = path.index(rbrace)
- except ValueError:
- res += dollar + brace + path
- index = pathlen - 1
- else:
- var = path[:index]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = dollar + brace + var + rbrace
- res += value
- else:
- var = path[:0]
- index += 1
- c = path[index:index + 1]
- while c and c in varchars:
- var += c
- index += 1
- c = path[index:index + 1]
- try:
- if environ is None:
- value = os.fsencode(os.environ[os.fsdecode(var)])
- else:
- value = environ[var]
- except KeyError:
- value = dollar + var
- res += value
- if c:
- index -= 1
+
+ def repl(m):
+ lastindex = m.lastindex
+ if lastindex is None:
+ return m.group(0)
+ name = m.group(lastindex)
+ if lastindex == 1:
+ if name == percent:
+ return name
+ if not name.endswith(percent):
+ return m.group(0)
+ name = name[:-1]
else:
- res += c
- index += 1
- return res
+ if name == dollar:
+ return name
+ if name.startswith(brace):
+ if not name.endswith(rbrace):
+ return m.group(0)
+ name = name[1:-1]
+
+ try:
+ if environ is None:
+ return os.fsencode(os.environ[os.fsdecode(name)])
+ else:
+ return environ[name]
+ except KeyError:
+ return m.group(0)
+
+ return sub(repl, path)
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
# Previously, this function also truncated pathnames to 8+3 format,
# but as this module is called "ntpath", that's obviously wrong!
+
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
- sep = b'\\'
- altsep = b'/'
- curdir = b'.'
- pardir = b'..'
- special_prefixes = (b'\\\\.\\', b'\\\\?\\')
+ sep = b"\\"
+ altsep = b"/"
+ curdir = b"."
+ pardir = b".."
+ special_prefixes = (b"\\\\.\\", b"\\\\?\\")
else:
- sep = '\\'
- altsep = '/'
- curdir = '.'
- pardir = '..'
- special_prefixes = ('\\\\.\\', '\\\\?\\')
+ sep = "\\"
+ altsep = "/"
+ curdir = "."
+ pardir = ".."
+ special_prefixes = ("\\\\.\\", "\\\\?\\")
if path.startswith(special_prefixes):
# in the case of paths with these prefixes:
# \\.\ -> device names
@@ -504,8 +532,8 @@
if not comps[i] or comps[i] == curdir:
del comps[i]
elif comps[i] == pardir:
- if i > 0 and comps[i-1] != pardir:
- del comps[i-1:i+1]
+ if i > 0 and comps[i - 1] != pardir:
+ del comps[i - 1 : i + 1]
i -= 1
elif i == 0 and prefix.endswith(sep):
del comps[i]
@@ -518,6 +546,7 @@
comps.append(curdir)
return prefix + sep.join(comps)
+
def _abspath_fallback(path):
"""Return the absolute version of a path as a fallback function in case
`nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
@@ -534,14 +563,16 @@
path = join(cwd, path)
return normpath(path)
+
# Return an absolute path.
try:
from nt import _getfullpathname
-except ImportError: # not running on Windows - mock up something sensible
+except ImportError: # not running on Windows - mock up something sensible
abspath = _abspath_fallback
else: # use native Windows method on Windows
+
def abspath(path):
"""Return the absolute version of a path."""
try:
@@ -549,23 +580,26 @@
except (OSError, ValueError):
return _abspath_fallback(path)
+
# realpath is a no-op on systems without islink support
realpath = abspath
# Win9x family and earlier have no Unicode filename support.
-supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
- sys.getwindowsversion()[3] >= 2)
+supports_unicode_filenames = (
+ hasattr(sys, "getwindowsversion") and sys.getwindowsversion()[3] >= 2
+)
+
def relpath(path, start=None):
"""Return a relative version of a path"""
path = os.fspath(path)
if isinstance(path, bytes):
- sep = b'\\'
- curdir = b'.'
- pardir = b'..'
+ sep = b"\\"
+ curdir = b"."
+ pardir = b".."
else:
- sep = '\\'
- curdir = '.'
- pardir = '..'
+ sep = "\\"
+ curdir = "."
+ pardir = ".."
if start is None:
start = curdir
@@ -580,8 +614,9 @@
start_drive, start_rest = splitdrive(start_abs)
path_drive, path_rest = splitdrive(path_abs)
if normcase(start_drive) != normcase(path_drive):
- raise ValueError("path is on mount %r, start on mount %r" % (
- path_drive, start_drive))
+ 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]
@@ -592,12 +627,12 @@
break
i += 1
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+ rel_list = [pardir] * (len(start_list) - i) + path_list[i:]
if not rel_list:
return curdir
return join(*rel_list)
except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
- genericpath._check_arg_types('relpath', path, start)
+ genericpath._check_arg_types("relpath", path, start)
raise
@@ -611,28 +646,29 @@
# first path given in the sequence. Additionally, any trailing separator is
# stripped from the returned path.
+
def commonpath(paths):
"""Given a sequence of path names, returns the longest common sub-path."""
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):
- sep = b'\\'
- altsep = b'/'
- curdir = b'.'
+ sep = b"\\"
+ altsep = b"/"
+ curdir = b"."
else:
- sep = '\\'
- altsep = '/'
- curdir = '.'
+ sep = "\\"
+ altsep = "/"
+ curdir = "."
try:
drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
split_paths = [p.split(sep) for d, p in drivesplits]
try:
- isabs, = set(p[:1] == sep for d, p in drivesplits)
+ (isabs,) = set(p[:1] == sep for d, p in drivesplits)
except ValueError:
raise ValueError("Can't mix absolute and relative paths") from None
@@ -654,12 +690,12 @@
common = common[:i]
break
else:
- common = common[:len(s1)]
+ common = common[: len(s1)]
prefix = drive + sep if isabs else drive
return prefix + sep.join(common)
except (TypeError, AttributeError):
- genericpath._check_arg_types('commonpath', *paths)
+ genericpath._check_arg_types("commonpath", *paths)
raise
Index: Python-3.6.15/Lib/posixpath.py
===================================================================
--- Python-3.6.15.orig/Lib/posixpath.py 2025-12-15 22:47:08.210375357 +0100
+++ Python-3.6.15/Lib/posixpath.py 2025-12-15 22:57:11.623135544 +0100
@@ -13,14 +13,14 @@
# Strings representing various path-related bits and pieces.
# These are primarily for export; internally, they are hardcoded.
# Should be set before imports for resolving cyclic dependency.
-curdir = '.'
-pardir = '..'
-extsep = '.'
-sep = '/'
-pathsep = ':'
-defpath = ':/bin:/usr/bin'
+curdir = "."
+pardir = ".."
+extsep = "."
+sep = "/"
+pathsep = ":"
+defpath = ":/bin:/usr/bin"
altsep = None
-devnull = '/dev/null'
+devnull = "/dev/null"
import os
import sys
@@ -28,39 +28,77 @@
import genericpath
from genericpath import *
-__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
- "basename","dirname","commonprefix","getsize","getmtime",
- "getatime","getctime","islink","exists","lexists","isdir","isfile",
- "ismount", "expanduser","expandvars","normpath","abspath",
- "samefile","sameopenfile","samestat",
- "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
- "devnull","realpath","supports_unicode_filenames","relpath",
- "commonpath", "ALLOW_MISSING"]
+__all__ = [
+ "normcase",
+ "isabs",
+ "join",
+ "splitdrive",
+ "split",
+ "splitext",
+ "basename",
+ "dirname",
+ "commonprefix",
+ "getsize",
+ "getmtime",
+ "getatime",
+ "getctime",
+ "islink",
+ "exists",
+ "lexists",
+ "isdir",
+ "isfile",
+ "ismount",
+ "expanduser",
+ "expandvars",
+ "normpath",
+ "abspath",
+ "samefile",
+ "sameopenfile",
+ "samestat",
+ "curdir",
+ "pardir",
+ "sep",
+ "pathsep",
+ "defpath",
+ "altsep",
+ "extsep",
+ "devnull",
+ "realpath",
+ "supports_unicode_filenames",
+ "relpath",
+ "commonpath",
+ "ALLOW_MISSING",
+]
def _get_sep(path):
if isinstance(path, bytes):
- return b'/'
+ return b"/"
else:
- return '/'
+ 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
# (another function should be defined to do that).
+
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__))
+ raise TypeError(
+ "normcase() argument must be str or bytes, "
+ "not '{}'".format(s.__class__.__name__)
+ )
return s
# Return whether a path is absolute.
# Trivial in Posix, harder on the Mac or MS-DOS.
+
def isabs(s):
"""Test whether a path is absolute"""
s = os.fspath(s)
@@ -72,6 +110,7 @@
# Ignore the previous parts if a part is absolute.
# Insert a '/' unless the first part is empty or already ends in '/'.
+
def join(a, *p):
"""Join two or more pathname components, inserting '/' as needed.
If any component is an absolute path, all previous path components
@@ -82,7 +121,7 @@
path = a
try:
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 map(os.fspath, p):
if b.startswith(sep):
path = b
@@ -91,7 +130,7 @@
else:
path += sep + b
except (TypeError, AttributeError, BytesWarning):
- genericpath._check_arg_types('join', a, *p)
+ genericpath._check_arg_types("join", a, *p)
raise
return path
@@ -101,6 +140,7 @@
# '/' in the path, head will be empty.
# Trailing '/'es are stripped from head unless it is the root.
+
def split(p):
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
everything after the final slash. Either part may be empty."""
@@ -108,7 +148,7 @@
sep = _get_sep(p)
i = p.rfind(sep) + 1
head, tail = p[:i], p[i:]
- if head and head != sep*len(head):
+ if head and head != sep * len(head):
head = head.rstrip(sep)
return head, tail
@@ -118,20 +158,24 @@
# pathname component; the root is everything before that.
# It is always true that root + ext == p.
+
def splitext(p):
p = os.fspath(p)
if isinstance(p, bytes):
- sep = b'/'
- extsep = b'.'
+ sep = b"/"
+ extsep = b"."
else:
- sep = '/'
- extsep = '.'
+ sep = "/"
+ extsep = "."
return genericpath._splitext(p, sep, None, extsep)
+
+
splitext.__doc__ = genericpath._splitext.__doc__
# Split a pathname into a drive specification and the rest of the
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
+
def splitdrive(p):
"""Split a pathname into drive and path. On Posix, drive is always
empty."""
@@ -141,6 +185,7 @@
# Return the tail (basename) part of a path, same as split(path)[1].
+
def basename(p):
"""Returns the final component of a pathname"""
p = os.fspath(p)
@@ -151,13 +196,14 @@
# Return the head (dirname) part of a path, same as split(path)[0].
+
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]
- if head and head != sep*len(head):
+ if head and head != sep * len(head):
head = head.rstrip(sep)
return head
@@ -165,6 +211,7 @@
# Is a path a symbolic link?
# This will always return false on systems where os.lstat doesn't exist.
+
def islink(path):
"""Test whether a path is a symbolic link"""
try:
@@ -173,8 +220,10 @@
return False
return stat.S_ISLNK(st.st_mode)
+
# Being true for dangling symbolic links is also useful.
+
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
@@ -187,6 +236,7 @@
# Is a path a mount point?
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
+
def ismount(path):
"""Test whether a path is a mount point"""
try:
@@ -200,9 +250,9 @@
return False
if isinstance(path, bytes):
- parent = join(path, b'..')
+ parent = join(path, b"..")
else:
- parent = join(path, '..')
+ parent = join(path, "..")
parent = realpath(parent)
try:
s2 = os.lstat(parent)
@@ -212,11 +262,11 @@
dev1 = s1.st_dev
dev2 = s2.st_dev
if dev1 != dev2:
- return True # path/.. on a different device as path
+ 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 True # path/.. is the same i-node as path
return False
@@ -229,14 +279,15 @@
# (A function should also be defined to do full *sh-style environment
# variable expansion.)
+
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'~'
+ tilde = b"~"
else:
- tilde = '~'
+ tilde = "~"
if not path.startswith(tilde):
return path
sep = _get_sep(path)
@@ -244,8 +295,9 @@
if i < 0:
i = len(path)
if i == 1:
- if 'HOME' not in os.environ:
+ if "HOME" not in os.environ:
import pwd
+
try:
userhome = pwd.getpwuid(os.getuid()).pw_dir
except KeyError:
@@ -253,12 +305,13 @@
# password database, return the path unchanged
return path
else:
- userhome = os.environ['HOME']
+ userhome = os.environ["HOME"]
else:
import pwd
+
name = path[1:i]
if isinstance(name, bytes):
- name = str(name, 'ASCII')
+ name = str(name, "ASCII")
try:
pwent = pwd.getpwnam(name)
except KeyError:
@@ -268,9 +321,9 @@
userhome = pwent.pw_dir
if isinstance(path, bytes):
userhome = os.fsencode(userhome)
- root = b'/'
+ root = b"/"
else:
- root = '/'
+ root = "/"
userhome = userhome.rstrip(root)
return (userhome + path[i:]) or root
@@ -279,42 +332,44 @@
# This expands the forms $variable and ${variable} only.
# Non-existent variables are left unchanged.
-_varprog = None
-_varprogb = None
+_varpattern = r"\$(\w+|\{[^}]*\}?)"
+_varsub = None
+_varsubb = None
+
def expandvars(path):
"""Expand shell variables of form $var and ${var}. Unknown variables
are left unchanged."""
path = os.fspath(path)
- global _varprog, _varprogb
+ global _varsub, _varsubb
if isinstance(path, bytes):
- if b'$' not in path:
+ if b"$" not in path:
return path
- if not _varprogb:
+ if not _varsubb:
import re
- _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
- search = _varprogb.search
- start = b'{'
- end = b'}'
- environ = getattr(os, 'environb', None)
+
+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
+ sub = _varsubb
+ start = b"{"
+ end = b"}"
+ environ = getattr(os, "environb", None)
else:
- if '$' not in path:
+ if "$" not in path:
return path
- if not _varprog:
+ if not _varsub:
import re
- _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
- search = _varprog.search
- start = '{'
- end = '}'
+
+ _varsub = re.compile(_varpattern, re.ASCII).sub
+ sub = _varsub
+ start = "{"
+ end = "}"
environ = os.environ
- i = 0
- while True:
- m = search(path, i)
- if not m:
- break
- i, j = m.span(0)
+
+ def repl(m):
name = m.group(1)
- if name.startswith(start) and name.endswith(end):
+ if name.startswith(start):
+ if not name.endswith(end):
+ return m.group(0)
name = name[1:-1]
try:
if environ is None:
@@ -322,54 +377,55 @@
else:
value = environ[name]
except KeyError:
- i = j
+ return m.group(0)
else:
- tail = path[j:]
- path = path[:i] + value
- i = len(path)
- path += tail
- return path
+ return value
+
+ return sub(repl, path)
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
# It should be understood that this may change the meaning of the path
# if it contains symbolic links!
+
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
- sep = b'/'
- empty = b''
- dot = b'.'
- dotdot = b'..'
- else:
- sep = '/'
- empty = ''
- dot = '.'
- dotdot = '..'
+ sep = b"/"
+ empty = b""
+ dot = b"."
+ dotdot = b".."
+ else:
+ sep = "/"
+ empty = ""
+ dot = "."
+ dotdot = ".."
if path == empty:
return dot
initial_slashes = path.startswith(sep)
# POSIX allows one or two initial slashes, but treats three or more
# as single slash.
- if (initial_slashes and
- path.startswith(sep*2) and not path.startswith(sep*3)):
+ if initial_slashes and path.startswith(sep * 2) and not path.startswith(sep * 3):
initial_slashes = 2
comps = path.split(sep)
new_comps = []
for comp in comps:
if comp in (empty, dot):
continue
- if (comp != dotdot or (not initial_slashes and not new_comps) or
- (new_comps and new_comps[-1] == dotdot)):
+ if (
+ comp != dotdot
+ or (not initial_slashes and not new_comps)
+ or (new_comps and new_comps[-1] == dotdot)
+ ):
new_comps.append(comp)
elif new_comps:
new_comps.pop()
comps = new_comps
path = sep.join(comps)
if initial_slashes:
- path = sep*initial_slashes + path
+ path = sep * initial_slashes + path
return path or dot
@@ -388,24 +444,26 @@
# Return a canonical path (i.e. the absolute location of a file on the
# filesystem).
+
def realpath(filename, *, strict=False):
"""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, strict, {})
return abspath(path)
+
# Join two paths, normalizing and eliminating any symbolic links
# encountered in the second path.
def _joinrealpath(path, rest, strict, seen):
if isinstance(path, bytes):
- sep = b'/'
- curdir = b'.'
- pardir = b'..'
- else:
- sep = '/'
- curdir = '.'
- pardir = '..'
+ sep = b"/"
+ curdir = b"."
+ pardir = b".."
+ else:
+ sep = "/"
+ curdir = "."
+ pardir = ".."
getcwd = os.getcwd
if strict is ALLOW_MISSING:
ignored_error = FileNotFoundError
@@ -458,16 +516,17 @@
else:
# Return already resolved part + rest of the path unchanged.
return join(newpath, rest), False
- seen[newpath] = None # not resolved symlink
+ seen[newpath] = None # not resolved symlink
path, ok = _joinrealpath(path, os.readlink(newpath), strict, seen)
if not ok:
return join(path, rest), False
- seen[newpath] = path # resolved symlink
+ seen[newpath] = path # resolved symlink
return path, True
-supports_unicode_filenames = (sys.platform == 'darwin')
+supports_unicode_filenames = sys.platform == "darwin"
+
def relpath(path, start=None):
"""Return a relative version of a path"""
@@ -477,13 +536,13 @@
path = os.fspath(path)
if isinstance(path, bytes):
- curdir = b'.'
- sep = b'/'
- pardir = b'..'
- else:
- curdir = '.'
- sep = '/'
- pardir = '..'
+ curdir = b"."
+ sep = b"/"
+ pardir = b".."
+ else:
+ curdir = "."
+ sep = "/"
+ pardir = ".."
if start is None:
start = curdir
@@ -496,12 +555,12 @@
# 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:]
+ rel_list = [pardir] * (len(start_list) - i) + path_list[i:]
if not rel_list:
return curdir
return join(*rel_list)
except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
- genericpath._check_arg_types('relpath', path, start)
+ genericpath._check_arg_types("relpath", path, start)
raise
@@ -510,25 +569,26 @@
# responsibility of the caller). Any trailing separator is stripped from the
# returned path.
+
def commonpath(paths):
"""Given a sequence of path names, returns the longest common sub-path."""
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):
- sep = b'/'
- curdir = b'.'
+ sep = b"/"
+ curdir = b"."
else:
- sep = '/'
- curdir = '.'
+ sep = "/"
+ curdir = "."
try:
split_paths = [path.split(sep) for path in paths]
try:
- isabs, = set(p[:1] == sep for p in paths)
+ (isabs,) = set(p[:1] == sep for p in paths)
except ValueError:
raise ValueError("Can't mix absolute and relative paths") from None
@@ -544,5 +604,5 @@
prefix = sep if isabs else sep[:0]
return prefix + sep.join(common)
except (TypeError, AttributeError):
- genericpath._check_arg_types('commonpath', *paths)
+ genericpath._check_arg_types("commonpath", *paths)
raise
Index: Python-3.6.15/Lib/test/test_genericpath.py
===================================================================
--- Python-3.6.15.orig/Lib/test/test_genericpath.py 2025-12-15 22:44:17.363776048 +0100
+++ Python-3.6.15/Lib/test/test_genericpath.py 2025-12-15 22:47:54.182942670 +0100
@@ -10,6 +10,7 @@
from test import support
from test.support.script_helper import assert_python_ok
from test.support import FakePath
+from test.support import EnvironmentVarGuard
def create_file(filename, data=b'foo'):
@@ -354,7 +355,7 @@
if self.pathmodule.__name__ == 'macpath':
self.skipTest('macpath.expandvars is a stub')
expandvars = self.pathmodule.expandvars
- with support.EnvironmentVarGuard() as env:
+ with EnvironmentVarGuard() as env:
env.clear()
env["foo"] = "bar"
env["{foo"] = "baz1"
@@ -390,7 +391,7 @@
expandvars = self.pathmodule.expandvars
def check(value, expected):
self.assertEqual(expandvars(value), expected)
- with support.EnvironmentVarGuard() as env:
+ with EnvironmentVarGuard() as env:
env.clear()
nonascii = support.FS_NONASCII
env['spam'] = nonascii
@@ -411,6 +412,19 @@
os.fsencode('$bar%s bar' % nonascii))
check(b'$spam}bar', os.fsencode('%s}bar' % nonascii))
+ @support.requires_resource('cpu')
+ def test_expandvars_large(self):
+ expandvars = self.pathmodule.expandvars
+ with EnvironmentVarGuard() as env:
+ env.clear()
+ env["A"] = "B"
+ n = 100000
+ self.assertEqual(expandvars('$A'*n), 'B'*n)
+ self.assertEqual(expandvars('${A}'*n), 'B'*n)
+ self.assertEqual(expandvars('$A!'*n), 'B!'*n)
+ self.assertEqual(expandvars('${A}A'*n), 'BA'*n)
+ self.assertEqual(expandvars('${'*10*n), '${'*10*n)
+
def test_abspath(self):
self.assertIn("foo", self.pathmodule.abspath("foo"))
with warnings.catch_warnings():
Index: Python-3.6.15/Lib/test/test_ntpath.py
===================================================================
--- Python-3.6.15.orig/Lib/test/test_ntpath.py 2025-12-15 22:47:08.211568204 +0100
+++ Python-3.6.15/Lib/test/test_ntpath.py 2025-12-15 22:48:53.187133106 +0100
@@ -5,7 +5,7 @@
import unittest
import warnings
from ntpath import ALLOW_MISSING
-from test.support import TestFailed, FakePath
+from test.support import TestFailed, FakePath, EnvironmentVarGuard
from test import support, test_genericpath
from tempfile import TemporaryFile
@@ -14,8 +14,10 @@
fn = fn.replace("\\", "\\\\")
gotResult = eval(fn)
if wantResult != gotResult:
- raise TestFailed("%s should return: %s but returned: %s" \
- %(str(fn), str(wantResult), str(gotResult)))
+ raise TestFailed(
+ "%s should return: %s but returned: %s"
+ % (str(fn), str(wantResult), str(gotResult))
+ )
# then with bytes
fn = fn.replace("('", "(b'")
@@ -24,8 +26,8 @@
fn = fn.replace('["', '[b"')
fn = fn.replace(", '", ", b'")
fn = fn.replace(', "', ', b"')
- fn = os.fsencode(fn).decode('latin1')
- fn = fn.encode('ascii', 'backslashreplace').decode('ascii')
+ fn = os.fsencode(fn).decode("latin1")
+ fn = fn.encode("ascii", "backslashreplace").decode("ascii")
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
gotResult = eval(fn)
@@ -36,78 +38,107 @@
gotResult = eval(fn)
if wantResult != gotResult:
- raise TestFailed("%s should return: %s but returned: %s" \
- %(str(fn), str(wantResult), repr(gotResult)))
+ raise TestFailed(
+ "%s should return: %s but returned: %s"
+ % (str(fn), str(wantResult), repr(gotResult))
+ )
class TestNtpath(unittest.TestCase):
def test_splitext(self):
- tester('ntpath.splitext("foo.ext")', ('foo', '.ext'))
- tester('ntpath.splitext("/foo/foo.ext")', ('/foo/foo', '.ext'))
- tester('ntpath.splitext(".ext")', ('.ext', ''))
- tester('ntpath.splitext("\\foo.ext\\foo")', ('\\foo.ext\\foo', ''))
- tester('ntpath.splitext("foo.ext\\")', ('foo.ext\\', ''))
- tester('ntpath.splitext("")', ('', ''))
- tester('ntpath.splitext("foo.bar.ext")', ('foo.bar', '.ext'))
- tester('ntpath.splitext("xx/foo.bar.ext")', ('xx/foo.bar', '.ext'))
- tester('ntpath.splitext("xx\\foo.bar.ext")', ('xx\\foo.bar', '.ext'))
- tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d'))
+ tester('ntpath.splitext("foo.ext")', ("foo", ".ext"))
+ tester('ntpath.splitext("/foo/foo.ext")', ("/foo/foo", ".ext"))
+ tester('ntpath.splitext(".ext")', (".ext", ""))
+ tester('ntpath.splitext("\\foo.ext\\foo")', ("\\foo.ext\\foo", ""))
+ tester('ntpath.splitext("foo.ext\\")', ("foo.ext\\", ""))
+ tester('ntpath.splitext("")', ("", ""))
+ tester('ntpath.splitext("foo.bar.ext")', ("foo.bar", ".ext"))
+ tester('ntpath.splitext("xx/foo.bar.ext")', ("xx/foo.bar", ".ext"))
+ tester('ntpath.splitext("xx\\foo.bar.ext")', ("xx\\foo.bar", ".ext"))
+ tester('ntpath.splitext("c:a/b\\c.d")', ("c:a/b\\c", ".d"))
def test_splitdrive(self):
- tester('ntpath.splitdrive("c:\\foo\\bar")',
- ('c:', '\\foo\\bar'))
- tester('ntpath.splitdrive("c:/foo/bar")',
- ('c:', '/foo/bar'))
- tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")',
- ('\\\\conky\\mountpoint', '\\foo\\bar'))
- tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")',
- ('//conky/mountpoint', '/foo/bar'))
- tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")',
- ('', '\\\\\\conky\\mountpoint\\foo\\bar'))
- tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")',
- ('', '///conky/mountpoint/foo/bar'))
- tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")',
- ('', '\\\\conky\\\\mountpoint\\foo\\bar'))
- tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")',
- ('', '//conky//mountpoint/foo/bar'))
+ tester('ntpath.splitdrive("c:\\foo\\bar")', ("c:", "\\foo\\bar"))
+ tester('ntpath.splitdrive("c:/foo/bar")', ("c:", "/foo/bar"))
+ tester(
+ 'ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")',
+ ("\\\\conky\\mountpoint", "\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitdrive("//conky/mountpoint/foo/bar")',
+ ("//conky/mountpoint", "/foo/bar"),
+ )
+ tester(
+ 'ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")',
+ ("", "\\\\\\conky\\mountpoint\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitdrive("///conky/mountpoint/foo/bar")',
+ ("", "///conky/mountpoint/foo/bar"),
+ )
+ tester(
+ 'ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")',
+ ("", "\\\\conky\\\\mountpoint\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitdrive("//conky//mountpoint/foo/bar")',
+ ("", "//conky//mountpoint/foo/bar"),
+ )
# Issue #19911: UNC part containing U+0130
- self.assertEqual(ntpath.splitdrive('//conky/MOUNTPOİNT/foo/bar'),
- ('//conky/MOUNTPOİNT', '/foo/bar'))
+ self.assertEqual(
+ ntpath.splitdrive("//conky/MOUNTPOİNT/foo/bar"),
+ ("//conky/MOUNTPOİNT", "/foo/bar"),
+ )
def test_splitunc(self):
with self.assertWarns(DeprecationWarning):
- ntpath.splitunc('')
- with support.check_warnings(('', DeprecationWarning)):
- tester('ntpath.splitunc("c:\\foo\\bar")',
- ('', 'c:\\foo\\bar'))
- tester('ntpath.splitunc("c:/foo/bar")',
- ('', 'c:/foo/bar'))
- tester('ntpath.splitunc("\\\\conky\\mountpoint\\foo\\bar")',
- ('\\\\conky\\mountpoint', '\\foo\\bar'))
- tester('ntpath.splitunc("//conky/mountpoint/foo/bar")',
- ('//conky/mountpoint', '/foo/bar'))
- tester('ntpath.splitunc("\\\\\\conky\\mountpoint\\foo\\bar")',
- ('', '\\\\\\conky\\mountpoint\\foo\\bar'))
- tester('ntpath.splitunc("///conky/mountpoint/foo/bar")',
- ('', '///conky/mountpoint/foo/bar'))
- tester('ntpath.splitunc("\\\\conky\\\\mountpoint\\foo\\bar")',
- ('', '\\\\conky\\\\mountpoint\\foo\\bar'))
- tester('ntpath.splitunc("//conky//mountpoint/foo/bar")',
- ('', '//conky//mountpoint/foo/bar'))
- self.assertEqual(ntpath.splitunc('//conky/MOUNTPOİNT/foo/bar'),
- ('//conky/MOUNTPOİNT', '/foo/bar'))
+ ntpath.splitunc("")
+ with support.check_warnings(("", DeprecationWarning)):
+ tester('ntpath.splitunc("c:\\foo\\bar")', ("", "c:\\foo\\bar"))
+ tester('ntpath.splitunc("c:/foo/bar")', ("", "c:/foo/bar"))
+ tester(
+ 'ntpath.splitunc("\\\\conky\\mountpoint\\foo\\bar")',
+ ("\\\\conky\\mountpoint", "\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitunc("//conky/mountpoint/foo/bar")',
+ ("//conky/mountpoint", "/foo/bar"),
+ )
+ tester(
+ 'ntpath.splitunc("\\\\\\conky\\mountpoint\\foo\\bar")',
+ ("", "\\\\\\conky\\mountpoint\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitunc("///conky/mountpoint/foo/bar")',
+ ("", "///conky/mountpoint/foo/bar"),
+ )
+ tester(
+ 'ntpath.splitunc("\\\\conky\\\\mountpoint\\foo\\bar")',
+ ("", "\\\\conky\\\\mountpoint\\foo\\bar"),
+ )
+ tester(
+ 'ntpath.splitunc("//conky//mountpoint/foo/bar")',
+ ("", "//conky//mountpoint/foo/bar"),
+ )
+ self.assertEqual(
+ ntpath.splitunc("//conky/MOUNTPOİNT/foo/bar"),
+ ("//conky/MOUNTPOİNT", "/foo/bar"),
+ )
def test_split(self):
- tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
- tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
- ('\\\\conky\\mountpoint\\foo', 'bar'))
-
- tester('ntpath.split("c:\\")', ('c:\\', ''))
- tester('ntpath.split("\\\\conky\\mountpoint\\")',
- ('\\\\conky\\mountpoint\\', ''))
+ tester('ntpath.split("c:\\foo\\bar")', ("c:\\foo", "bar"))
+ tester(
+ 'ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
+ ("\\\\conky\\mountpoint\\foo", "bar"),
+ )
+
+ tester('ntpath.split("c:\\")', ("c:\\", ""))
+ tester(
+ 'ntpath.split("\\\\conky\\mountpoint\\")', ("\\\\conky\\mountpoint\\", "")
+ )
- tester('ntpath.split("c:/")', ('c:/', ''))
- tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
+ tester('ntpath.split("c:/")', ("c:/", ""))
+ tester('ntpath.split("//conky/mountpoint/")', ("//conky/mountpoint/", ""))
def test_isabs(self):
tester('ntpath.isabs("c:\\")', 1)
@@ -116,107 +147,139 @@
tester('ntpath.isabs("\\foo\\bar")', 1)
def test_commonprefix(self):
- tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])',
- "/home/swen")
- tester('ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])',
- "\\home\\swen\\")
- tester('ntpath.commonprefix(["/home/swen/spam", "/home/swen/spam"])',
- "/home/swen/spam")
+ tester(
+ 'ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])',
+ "/home/swen",
+ )
+ tester(
+ 'ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])',
+ "\\home\\swen\\",
+ )
+ tester(
+ 'ntpath.commonprefix(["/home/swen/spam", "/home/swen/spam"])',
+ "/home/swen/spam",
+ )
def test_join(self):
- tester('ntpath.join("")', '')
- tester('ntpath.join("", "", "")', '')
- tester('ntpath.join("a")', 'a')
- tester('ntpath.join("/a")', '/a')
- tester('ntpath.join("\\a")', '\\a')
- tester('ntpath.join("a:")', 'a:')
- tester('ntpath.join("a:", "\\b")', 'a:\\b')
- tester('ntpath.join("a", "\\b")', '\\b')
- tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
- tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
- tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
- tester('ntpath.join("a", "b", "\\c")', '\\c')
- tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
- tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
-
- tester("ntpath.join('', 'a')", 'a')
- tester("ntpath.join('', '', '', '', 'a')", 'a')
- tester("ntpath.join('a', '')", 'a\\')
- tester("ntpath.join('a', '', '', '', '')", 'a\\')
- tester("ntpath.join('a\\', '')", 'a\\')
- tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
- tester("ntpath.join('a/', '')", 'a/')
-
- tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
- tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
- tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
- tester("ntpath.join('c:', 'x/y')", 'c:x/y')
- tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
- tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
- tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
- tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
- tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
- tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
- tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
- tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
-
- tester("ntpath.join('a/b', '/x/y')", '/x/y')
- tester("ntpath.join('/a/b', '/x/y')", '/x/y')
- tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
- tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
- tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
- tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
- tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
- tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
- tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
-
- tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
- tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
- tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
- tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
-
- for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b',
- '//computer/share', '//computer/share/', '//computer/share/a/b'):
- for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y',
- '//machine/common', '//machine/common/', '//machine/common/x/y'):
+ tester('ntpath.join("")', "")
+ tester('ntpath.join("", "", "")', "")
+ tester('ntpath.join("a")', "a")
+ tester('ntpath.join("/a")', "/a")
+ tester('ntpath.join("\\a")', "\\a")
+ tester('ntpath.join("a:")', "a:")
+ tester('ntpath.join("a:", "\\b")', "a:\\b")
+ tester('ntpath.join("a", "\\b")', "\\b")
+ tester('ntpath.join("a", "b", "c")', "a\\b\\c")
+ tester('ntpath.join("a\\", "b", "c")', "a\\b\\c")
+ tester('ntpath.join("a", "b\\", "c")', "a\\b\\c")
+ tester('ntpath.join("a", "b", "\\c")', "\\c")
+ tester('ntpath.join("d:\\", "\\pleep")', "d:\\pleep")
+ tester('ntpath.join("d:\\", "a", "b")', "d:\\a\\b")
+
+ tester("ntpath.join('', 'a')", "a")
+ tester("ntpath.join('', '', '', '', 'a')", "a")
+ tester("ntpath.join('a', '')", "a\\")
+ tester("ntpath.join('a', '', '', '', '')", "a\\")
+ tester("ntpath.join('a\\', '')", "a\\")
+ tester("ntpath.join('a\\', '', '', '', '')", "a\\")
+ tester("ntpath.join('a/', '')", "a/")
+
+ tester("ntpath.join('a/b', 'x/y')", "a/b\\x/y")
+ tester("ntpath.join('/a/b', 'x/y')", "/a/b\\x/y")
+ tester("ntpath.join('/a/b/', 'x/y')", "/a/b/x/y")
+ tester("ntpath.join('c:', 'x/y')", "c:x/y")
+ tester("ntpath.join('c:a/b', 'x/y')", "c:a/b\\x/y")
+ tester("ntpath.join('c:a/b/', 'x/y')", "c:a/b/x/y")
+ tester("ntpath.join('c:/', 'x/y')", "c:/x/y")
+ tester("ntpath.join('c:/a/b', 'x/y')", "c:/a/b\\x/y")
+ tester("ntpath.join('c:/a/b/', 'x/y')", "c:/a/b/x/y")
+ tester("ntpath.join('//computer/share', 'x/y')", "//computer/share\\x/y")
+ tester("ntpath.join('//computer/share/', 'x/y')", "//computer/share/x/y")
+ tester(
+ "ntpath.join('//computer/share/a/b', 'x/y')", "//computer/share/a/b\\x/y"
+ )
+
+ tester("ntpath.join('a/b', '/x/y')", "/x/y")
+ tester("ntpath.join('/a/b', '/x/y')", "/x/y")
+ tester("ntpath.join('c:', '/x/y')", "c:/x/y")
+ tester("ntpath.join('c:a/b', '/x/y')", "c:/x/y")
+ tester("ntpath.join('c:/', '/x/y')", "c:/x/y")
+ tester("ntpath.join('c:/a/b', '/x/y')", "c:/x/y")
+ tester("ntpath.join('//computer/share', '/x/y')", "//computer/share/x/y")
+ tester("ntpath.join('//computer/share/', '/x/y')", "//computer/share/x/y")
+ tester("ntpath.join('//computer/share/a', '/x/y')", "//computer/share/x/y")
+
+ tester("ntpath.join('c:', 'C:x/y')", "C:x/y")
+ tester("ntpath.join('c:a/b', 'C:x/y')", "C:a/b\\x/y")
+ tester("ntpath.join('c:/', 'C:x/y')", "C:/x/y")
+ tester("ntpath.join('c:/a/b', 'C:x/y')", "C:/a/b\\x/y")
+
+ for x in (
+ "",
+ "a/b",
+ "/a/b",
+ "c:",
+ "c:a/b",
+ "c:/",
+ "c:/a/b",
+ "//computer/share",
+ "//computer/share/",
+ "//computer/share/a/b",
+ ):
+ for y in (
+ "d:",
+ "d:x/y",
+ "d:/",
+ "d:/x/y",
+ "//machine/common",
+ "//machine/common/",
+ "//machine/common/x/y",
+ ):
tester("ntpath.join(%r, %r)" % (x, y), y)
- tester("ntpath.join('\\\\computer\\share\\', 'a', 'b')", '\\\\computer\\share\\a\\b')
- tester("ntpath.join('\\\\computer\\share', 'a', 'b')", '\\\\computer\\share\\a\\b')
- tester("ntpath.join('\\\\computer\\share', 'a\\b')", '\\\\computer\\share\\a\\b')
- tester("ntpath.join('//computer/share/', 'a', 'b')", '//computer/share/a\\b')
- tester("ntpath.join('//computer/share', 'a', 'b')", '//computer/share\\a\\b')
- tester("ntpath.join('//computer/share', 'a/b')", '//computer/share\\a/b')
+ tester(
+ "ntpath.join('\\\\computer\\share\\', 'a', 'b')",
+ "\\\\computer\\share\\a\\b",
+ )
+ tester(
+ "ntpath.join('\\\\computer\\share', 'a', 'b')", "\\\\computer\\share\\a\\b"
+ )
+ tester(
+ "ntpath.join('\\\\computer\\share', 'a\\b')", "\\\\computer\\share\\a\\b"
+ )
+ tester("ntpath.join('//computer/share/', 'a', 'b')", "//computer/share/a\\b")
+ tester("ntpath.join('//computer/share', 'a', 'b')", "//computer/share\\a\\b")
+ tester("ntpath.join('//computer/share', 'a/b')", "//computer/share\\a/b")
def test_normpath(self):
- tester("ntpath.normpath('A//////././//.//B')", r'A\B')
- tester("ntpath.normpath('A/./B')", r'A\B')
- tester("ntpath.normpath('A/foo/../B')", r'A\B')
- tester("ntpath.normpath('C:A//B')", r'C:A\B')
- tester("ntpath.normpath('D:A/./B')", r'D:A\B')
- tester("ntpath.normpath('e:A/foo/../B')", r'e:A\B')
-
- tester("ntpath.normpath('C:///A//B')", r'C:\A\B')
- tester("ntpath.normpath('D:///A/./B')", r'D:\A\B')
- tester("ntpath.normpath('e:///A/foo/../B')", r'e:\A\B')
-
- tester("ntpath.normpath('..')", r'..')
- tester("ntpath.normpath('.')", r'.')
- tester("ntpath.normpath('')", r'.')
- tester("ntpath.normpath('/')", '\\')
- tester("ntpath.normpath('c:/')", 'c:\\')
- tester("ntpath.normpath('/../.././..')", '\\')
- tester("ntpath.normpath('c:/../../..')", 'c:\\')
- tester("ntpath.normpath('../.././..')", r'..\..\..')
- tester("ntpath.normpath('K:../.././..')", r'K:..\..\..')
- tester("ntpath.normpath('C:////a/b')", r'C:\a\b')
- tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b')
+ tester("ntpath.normpath('A//////././//.//B')", r"A\B")
+ tester("ntpath.normpath('A/./B')", r"A\B")
+ tester("ntpath.normpath('A/foo/../B')", r"A\B")
+ tester("ntpath.normpath('C:A//B')", r"C:A\B")
+ tester("ntpath.normpath('D:A/./B')", r"D:A\B")
+ tester("ntpath.normpath('e:A/foo/../B')", r"e:A\B")
+
+ tester("ntpath.normpath('C:///A//B')", r"C:\A\B")
+ tester("ntpath.normpath('D:///A/./B')", r"D:\A\B")
+ tester("ntpath.normpath('e:///A/foo/../B')", r"e:\A\B")
+
+ tester("ntpath.normpath('..')", r"..")
+ tester("ntpath.normpath('.')", r".")
+ tester("ntpath.normpath('')", r".")
+ tester("ntpath.normpath('/')", "\\")
+ tester("ntpath.normpath('c:/')", "c:\\")
+ tester("ntpath.normpath('/../.././..')", "\\")
+ tester("ntpath.normpath('c:/../../..')", "c:\\")
+ tester("ntpath.normpath('../.././..')", r"..\..\..")
+ tester("ntpath.normpath('K:../.././..')", r"K:..\..\..")
+ tester("ntpath.normpath('C:////a/b')", r"C:\a\b")
+ tester("ntpath.normpath('//machine/share//a/b')", r"\\machine\share\a\b")
- tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL')
- tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z')
+ tester("ntpath.normpath('\\\\.\\NUL')", r"\\.\NUL")
+ tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r"\\?\D:/XY\Z")
def test_expandvars(self):
- with support.EnvironmentVarGuard() as env:
+ with EnvironmentVarGuard() as env:
env.clear()
env["foo"] = "bar"
env["{foo"] = "baz1"
@@ -238,63 +301,73 @@
tester('ntpath.expandvars("%%foo%%foo%foo%")', "%foo%foobar")
tester('ntpath.expandvars("%?bar%")', "%?bar%")
tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
- tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
- tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%")
+ tester("ntpath.expandvars(\"'%foo%'%bar\")", "'%foo%'%bar")
+ tester('ntpath.expandvars("bar\'%foo%")', "bar'%foo%")
- @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII')
+ @unittest.skipUnless(support.FS_NONASCII, "need support.FS_NONASCII")
def test_expandvars_nonascii(self):
def check(value, expected):
- tester('ntpath.expandvars(%r)' % value, expected)
- with support.EnvironmentVarGuard() as env:
+ tester("ntpath.expandvars(%r)" % value, expected)
+
+ with EnvironmentVarGuard() as env:
env.clear()
nonascii = support.FS_NONASCII
- env['spam'] = nonascii
- env[nonascii] = 'ham' + nonascii
- check('$spam bar', '%s bar' % nonascii)
- check('$%s bar' % nonascii, '$%s bar' % nonascii)
- check('${spam}bar', '%sbar' % nonascii)
- check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
- check('$spam}bar', '%s}bar' % nonascii)
- check('$%s}bar' % nonascii, '$%s}bar' % nonascii)
- check('%spam% bar', '%s bar' % nonascii)
- check('%{}% bar'.format(nonascii), 'ham%s bar' % nonascii)
- check('%spam%bar', '%sbar' % nonascii)
- check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
+ env["spam"] = nonascii
+ env[nonascii] = "ham" + nonascii
+ check("$spam bar", "%s bar" % nonascii)
+ check("$%s bar" % nonascii, "$%s bar" % nonascii)
+ check("${spam}bar", "%sbar" % nonascii)
+ check("${%s}bar" % nonascii, "ham%sbar" % nonascii)
+ check("$spam}bar", "%s}bar" % nonascii)
+ check("$%s}bar" % nonascii, "$%s}bar" % nonascii)
+ check("%spam% bar", "%s bar" % nonascii)
+ check("%{}% bar".format(nonascii), "ham%s bar" % nonascii)
+ check("%spam%bar", "%sbar" % nonascii)
+ check("%{}%bar".format(nonascii), "ham%sbar" % nonascii)
+
+ @support.requires_resource("cpu")
+ def test_expandvars_large(self):
+ expandvars = ntpath.expandvars
+ with EnvironmentVarGuard() as env:
+ env.clear()
+ env["A"] = "B"
+ n = 100000
+ self.assertEqual(expandvars("%A%" * n), "B" * n)
+ self.assertEqual(expandvars("%A%A" * n), "BA" * n)
+ self.assertEqual(expandvars("''" * n + "%%"), "''" * n + "%")
+ self.assertEqual(expandvars("%%" * n), "%" * n)
+ self.assertEqual(expandvars("$$" * n), "$" * n)
def test_expanduser(self):
- tester('ntpath.expanduser("test")', 'test')
+ tester('ntpath.expanduser("test")', "test")
- with support.EnvironmentVarGuard() as env:
+ with EnvironmentVarGuard() as env:
env.clear()
- tester('ntpath.expanduser("~test")', '~test')
+ tester('ntpath.expanduser("~test")', "~test")
- env['HOMEPATH'] = 'eric\\idle'
- env['HOMEDRIVE'] = 'C:\\'
- tester('ntpath.expanduser("~test")', 'C:\\eric\\test')
- tester('ntpath.expanduser("~")', 'C:\\eric\\idle')
-
- del env['HOMEDRIVE']
- tester('ntpath.expanduser("~test")', 'eric\\test')
- tester('ntpath.expanduser("~")', 'eric\\idle')
+ env["HOMEPATH"] = "eric\\idle"
+ env["HOMEDRIVE"] = "C:\\"
+ tester('ntpath.expanduser("~test")', "C:\\eric\\test")
+ tester('ntpath.expanduser("~")', "C:\\eric\\idle")
+
+ del env["HOMEDRIVE"]
+ tester('ntpath.expanduser("~test")', "eric\\test")
+ tester('ntpath.expanduser("~")', "eric\\idle")
env.clear()
- env['USERPROFILE'] = 'C:\\eric\\idle'
- tester('ntpath.expanduser("~test")', 'C:\\eric\\test')
- tester('ntpath.expanduser("~")', 'C:\\eric\\idle')
+ env["USERPROFILE"] = "C:\\eric\\idle"
+ tester('ntpath.expanduser("~test")', "C:\\eric\\test")
+ tester('ntpath.expanduser("~")', "C:\\eric\\idle")
env.clear()
- env['HOME'] = 'C:\\idle\\eric'
- tester('ntpath.expanduser("~test")', 'C:\\idle\\test')
- tester('ntpath.expanduser("~")', 'C:\\idle\\eric')
-
- tester('ntpath.expanduser("~test\\foo\\bar")',
- 'C:\\idle\\test\\foo\\bar')
- tester('ntpath.expanduser("~test/foo/bar")',
- 'C:\\idle\\test/foo/bar')
- tester('ntpath.expanduser("~\\foo\\bar")',
- 'C:\\idle\\eric\\foo\\bar')
- tester('ntpath.expanduser("~/foo/bar")',
- 'C:\\idle\\eric/foo/bar')
+ env["HOME"] = "C:\\idle\\eric"
+ tester('ntpath.expanduser("~test")', "C:\\idle\\test")
+ tester('ntpath.expanduser("~")', "C:\\idle\\eric")
+
+ tester('ntpath.expanduser("~test\\foo\\bar")', "C:\\idle\\test\\foo\\bar")
+ tester('ntpath.expanduser("~test/foo/bar")', "C:\\idle\\test/foo/bar")
+ tester('ntpath.expanduser("~\\foo\\bar")', "C:\\idle\\eric\\foo\\bar")
+ tester('ntpath.expanduser("~/foo/bar")', "C:\\idle\\eric/foo/bar")
def test_abspath(self):
# ntpath.abspath() can only be used on a system with the "nt" module
@@ -304,108 +377,112 @@
# from any platform.
try:
import nt
+
tester('ntpath.abspath("C:\\")', "C:\\")
- with support.temp_cwd(support.TESTFN) as cwd_dir: # bpo-31047
+ with support.temp_cwd(support.TESTFN) as cwd_dir: # bpo-31047
tester('ntpath.abspath("")', cwd_dir)
tester('ntpath.abspath(" ")', cwd_dir + "\\ ")
tester('ntpath.abspath("?")', cwd_dir + "\\?")
drive, _ = ntpath.splitdrive(cwd_dir)
tester('ntpath.abspath("/abc/")', drive + "\\abc")
except ImportError:
- self.skipTest('nt module not available')
+ self.skipTest("nt module not available")
def test_relpath(self):
- tester('ntpath.relpath("a")', 'a')
- tester('ntpath.relpath(os.path.abspath("a"))', 'a')
- tester('ntpath.relpath("a/b")', 'a\\b')
- tester('ntpath.relpath("../a/b")', '..\\a\\b')
+ tester('ntpath.relpath("a")', "a")
+ tester('ntpath.relpath(os.path.abspath("a"))', "a")
+ tester('ntpath.relpath("a/b")', "a\\b")
+ tester('ntpath.relpath("../a/b")', "..\\a\\b")
with support.temp_cwd(support.TESTFN) as cwd_dir:
currentdir = os.path.basename(cwd_dir)
- tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a')
- tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b')
- tester('ntpath.relpath("a", "b/c")', '..\\..\\a')
- tester('ntpath.relpath("c:/foo/bar/bat", "c:/x/y")', '..\\..\\foo\\bar\\bat')
- tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a')
- tester('ntpath.relpath("a", "a")', '.')
- tester('ntpath.relpath("/foo/bar/bat", "/x/y/z")', '..\\..\\..\\foo\\bar\\bat')
- tester('ntpath.relpath("/foo/bar/bat", "/foo/bar")', 'bat')
- tester('ntpath.relpath("/foo/bar/bat", "/")', 'foo\\bar\\bat')
- tester('ntpath.relpath("/", "/foo/bar/bat")', '..\\..\\..')
- tester('ntpath.relpath("/foo/bar/bat", "/x")', '..\\foo\\bar\\bat')
- tester('ntpath.relpath("/x", "/foo/bar/bat")', '..\\..\\..\\x')
- tester('ntpath.relpath("/", "/")', '.')
- tester('ntpath.relpath("/a", "/a")', '.')
- tester('ntpath.relpath("/a/b", "/a/b")', '.')
- tester('ntpath.relpath("c:/foo", "C:/FOO")', '.')
+ tester('ntpath.relpath("a", "../b")', "..\\" + currentdir + "\\a")
+ tester('ntpath.relpath("a/b", "../c")', "..\\" + currentdir + "\\a\\b")
+ tester('ntpath.relpath("a", "b/c")', "..\\..\\a")
+ tester('ntpath.relpath("c:/foo/bar/bat", "c:/x/y")', "..\\..\\foo\\bar\\bat")
+ tester(
+ 'ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")',
+ "..\\..\\a",
+ )
+ tester('ntpath.relpath("a", "a")', ".")
+ tester('ntpath.relpath("/foo/bar/bat", "/x/y/z")', "..\\..\\..\\foo\\bar\\bat")
+ tester('ntpath.relpath("/foo/bar/bat", "/foo/bar")', "bat")
+ tester('ntpath.relpath("/foo/bar/bat", "/")', "foo\\bar\\bat")
+ tester('ntpath.relpath("/", "/foo/bar/bat")', "..\\..\\..")
+ tester('ntpath.relpath("/foo/bar/bat", "/x")', "..\\foo\\bar\\bat")
+ tester('ntpath.relpath("/x", "/foo/bar/bat")', "..\\..\\..\\x")
+ tester('ntpath.relpath("/", "/")', ".")
+ tester('ntpath.relpath("/a", "/a")', ".")
+ tester('ntpath.relpath("/a/b", "/a/b")', ".")
+ tester('ntpath.relpath("c:/foo", "C:/FOO")', ".")
def test_commonpath(self):
def check(paths, expected):
- tester(('ntpath.commonpath(%r)' % paths).replace('\\\\', '\\'),
- expected)
+ tester(("ntpath.commonpath(%r)" % paths).replace("\\\\", "\\"), expected)
+
def check_error(exc, paths):
self.assertRaises(exc, ntpath.commonpath, paths)
- self.assertRaises(exc, ntpath.commonpath,
- [os.fsencode(p) for p in paths])
+ self.assertRaises(exc, ntpath.commonpath, [os.fsencode(p) for p in paths])
self.assertRaises(ValueError, ntpath.commonpath, [])
- check_error(ValueError, ['C:\\Program Files', 'Program Files'])
- check_error(ValueError, ['C:\\Program Files', 'C:Program Files'])
- check_error(ValueError, ['\\Program Files', 'Program Files'])
- check_error(ValueError, ['Program Files', 'C:\\Program Files'])
- check(['C:\\Program Files'], 'C:\\Program Files')
- check(['C:\\Program Files', 'C:\\Program Files'], 'C:\\Program Files')
- check(['C:\\Program Files\\', 'C:\\Program Files'],
- 'C:\\Program Files')
- check(['C:\\Program Files\\', 'C:\\Program Files\\'],
- 'C:\\Program Files')
- check(['C:\\\\Program Files', 'C:\\Program Files\\\\'],
- 'C:\\Program Files')
- check(['C:\\.\\Program Files', 'C:\\Program Files\\.'],
- 'C:\\Program Files')
- check(['C:\\', 'C:\\bin'], 'C:\\')
- check(['C:\\Program Files', 'C:\\bin'], 'C:\\')
- check(['C:\\Program Files', 'C:\\Program Files\\Bar'],
- 'C:\\Program Files')
- check(['C:\\Program Files\\Foo', 'C:\\Program Files\\Bar'],
- 'C:\\Program Files')
- check(['C:\\Program Files', 'C:\\Projects'], 'C:\\')
- check(['C:\\Program Files\\', 'C:\\Projects'], 'C:\\')
-
- check(['C:\\Program Files\\Foo', 'C:/Program Files/Bar'],
- 'C:\\Program Files')
- check(['C:\\Program Files\\Foo', 'c:/program files/bar'],
- 'C:\\Program Files')
- check(['c:/program files/bar', 'C:\\Program Files\\Foo'],
- 'c:\\program files')
-
- check_error(ValueError, ['C:\\Program Files', 'D:\\Program Files'])
-
- check(['spam'], 'spam')
- check(['spam', 'spam'], 'spam')
- check(['spam', 'alot'], '')
- check(['and\\jam', 'and\\spam'], 'and')
- check(['and\\\\jam', 'and\\spam\\\\'], 'and')
- check(['and\\.\\jam', '.\\and\\spam'], 'and')
- check(['and\\jam', 'and\\spam', 'alot'], '')
- check(['and\\jam', 'and\\spam', 'and'], 'and')
- check(['C:and\\jam', 'C:and\\spam'], 'C:and')
-
- check([''], '')
- check(['', 'spam\\alot'], '')
- check_error(ValueError, ['', '\\spam\\alot'])
-
- self.assertRaises(TypeError, ntpath.commonpath,
- [b'C:\\Program Files', 'C:\\Program Files\\Foo'])
- self.assertRaises(TypeError, ntpath.commonpath,
- [b'C:\\Program Files', 'Program Files\\Foo'])
- self.assertRaises(TypeError, ntpath.commonpath,
- [b'Program Files', 'C:\\Program Files\\Foo'])
- self.assertRaises(TypeError, ntpath.commonpath,
- ['C:\\Program Files', b'C:\\Program Files\\Foo'])
- self.assertRaises(TypeError, ntpath.commonpath,
- ['C:\\Program Files', b'Program Files\\Foo'])
- self.assertRaises(TypeError, ntpath.commonpath,
- ['Program Files', b'C:\\Program Files\\Foo'])
+ check_error(ValueError, ["C:\\Program Files", "Program Files"])
+ check_error(ValueError, ["C:\\Program Files", "C:Program Files"])
+ check_error(ValueError, ["\\Program Files", "Program Files"])
+ check_error(ValueError, ["Program Files", "C:\\Program Files"])
+ check(["C:\\Program Files"], "C:\\Program Files")
+ check(["C:\\Program Files", "C:\\Program Files"], "C:\\Program Files")
+ check(["C:\\Program Files\\", "C:\\Program Files"], "C:\\Program Files")
+ check(["C:\\Program Files\\", "C:\\Program Files\\"], "C:\\Program Files")
+ check(["C:\\\\Program Files", "C:\\Program Files\\\\"], "C:\\Program Files")
+ check(["C:\\.\\Program Files", "C:\\Program Files\\."], "C:\\Program Files")
+ check(["C:\\", "C:\\bin"], "C:\\")
+ check(["C:\\Program Files", "C:\\bin"], "C:\\")
+ check(["C:\\Program Files", "C:\\Program Files\\Bar"], "C:\\Program Files")
+ check(["C:\\Program Files\\Foo", "C:\\Program Files\\Bar"], "C:\\Program Files")
+ check(["C:\\Program Files", "C:\\Projects"], "C:\\")
+ check(["C:\\Program Files\\", "C:\\Projects"], "C:\\")
+
+ check(["C:\\Program Files\\Foo", "C:/Program Files/Bar"], "C:\\Program Files")
+ check(["C:\\Program Files\\Foo", "c:/program files/bar"], "C:\\Program Files")
+ check(["c:/program files/bar", "C:\\Program Files\\Foo"], "c:\\program files")
+
+ check_error(ValueError, ["C:\\Program Files", "D:\\Program Files"])
+
+ check(["spam"], "spam")
+ check(["spam", "spam"], "spam")
+ check(["spam", "alot"], "")
+ check(["and\\jam", "and\\spam"], "and")
+ check(["and\\\\jam", "and\\spam\\\\"], "and")
+ check(["and\\.\\jam", ".\\and\\spam"], "and")
+ check(["and\\jam", "and\\spam", "alot"], "")
+ check(["and\\jam", "and\\spam", "and"], "and")
+ check(["C:and\\jam", "C:and\\spam"], "C:and")
+
+ check([""], "")
+ check(["", "spam\\alot"], "")
+ check_error(ValueError, ["", "\\spam\\alot"])
+
+ self.assertRaises(
+ TypeError,
+ ntpath.commonpath,
+ [b"C:\\Program Files", "C:\\Program Files\\Foo"],
+ )
+ self.assertRaises(
+ TypeError, ntpath.commonpath, [b"C:\\Program Files", "Program Files\\Foo"]
+ )
+ self.assertRaises(
+ TypeError, ntpath.commonpath, [b"Program Files", "C:\\Program Files\\Foo"]
+ )
+ self.assertRaises(
+ TypeError,
+ ntpath.commonpath,
+ ["C:\\Program Files", b"C:\\Program Files\\Foo"],
+ )
+ self.assertRaises(
+ TypeError, ntpath.commonpath, ["C:\\Program Files", b"Program Files\\Foo"]
+ )
+ self.assertRaises(
+ TypeError, ntpath.commonpath, ["Program Files", b"C:\\Program Files\\Foo"]
+ )
def test_sameopenfile(self):
with TemporaryFile() as tf1, TemporaryFile() as tf2:
@@ -455,9 +532,10 @@
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
+
class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
pathmodule = ntpath
- attributes = ['relpath', 'splitunc']
+ attributes = ["relpath", "splitunc"]
class PathLikeTests(unittest.TestCase):
@@ -468,7 +546,7 @@
self.file_name = support.TESTFN.lower()
self.file_path = FakePath(support.TESTFN)
self.addCleanup(support.unlink, self.file_name)
- with open(self.file_name, 'xb', 0) as file:
+ with open(self.file_name, "xb", 0) as file:
file.write(b"test_ntpath.PathLikeTests")
def assertPathEqual(self, func):
@@ -481,8 +559,9 @@
self.assertPathEqual(self.path.isabs)
def test_path_join(self):
- self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
- self.path.join('a', 'b', 'c'))
+ self.assertEqual(
+ self.path.join("a", FakePath("b"), "c"), self.path.join("a", "b", "c")
+ )
def test_path_split(self):
self.assertPathEqual(self.path.split)
Index: Python-3.6.15/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ Python-3.6.15/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst 2025-12-15 22:47:15.854142448 +0100
@@ -0,0 +1 @@
+Fix quadratic complexity in :func:`os.path.expandvars`.