From 15aadf9b0e4551c157c04ae60592c84e4caae01d Mon Sep 17 00:00:00 2001 From: Maor Kleinberger Date: Sat, 16 Mar 2019 00:07:30 +0200 Subject: [PATCH 1/9] Fixed bugs with drive-relative paths and NTFS ADS paths in pathlib --- Lib/pathlib.py | 80 ++++++++++++------- Lib/test/test_pathlib.py | 38 ++++++++- .../2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst | 1 + 3 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 6369c4b2218d33..3c4836671ffa17 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -60,38 +60,49 @@ class _Flavour(object): def __init__(self): self.join = self.sep.join + def _split_part(self, part): + """ + Return the drive, root and path parts from a given part. + If the part is a tuple, it already contains these values and therefore is returned. + Otherwise, splitroot is used to parse the part. + """ + if isinstance(part, tuple): + return part + elif isinstance(part, str): + if self.altsep: + part = part.replace(self.altsep, self.sep) + drv, root, rel = self.splitroot(part) + return drv, root, rel.split(self.sep) + else: + raise TypeError(f'argument should be a tuple or an str object, not {type(part)}') + def parse_parts(self, parts): + """ + Parse and join multiple path strings, and + return a tuple of the final drive, root and path parts. + The given parts can be either strings of paths, + or tuples that represent paths, containing the drive, root and list of path parts. + The option for passing a tuple is needed, as the part 'a:b' could be interpreted + either as the relative path 'b' with the drive 'a:', + or as a file 'a' with the NTFS data-stream 'b'. + For example, passing either ('a:', '', ['b']) or ('', '', ['a:b']) instead of 'a:b' + will allow parse_parts to behave properly in these cases. + """ parsed = [] - sep = self.sep - altsep = self.altsep drv = root = '' it = reversed(parts) for part in it: if not part: continue - if altsep: - part = part.replace(altsep, sep) - drv, root, rel = self.splitroot(part) - if sep in rel: - for x in reversed(rel.split(sep)): + current_drv, current_root, rel_parts = self._split_part(part) + if not drv: + drv = current_drv + if not root: + root = current_root + for x in reversed(rel_parts): if x and x != '.': parsed.append(sys.intern(x)) - else: - if rel and rel != '.': - parsed.append(sys.intern(rel)) - if drv or root: - if not drv: - # If no drive is present, try to find one in the previous - # parts. This makes the result of parsing e.g. - # ("C:", "/", "a") reasonably intuitive. - for part in it: - if not part: - continue - if altsep: - part = part.replace(altsep, sep) - drv = self.splitroot(part)[0] - if drv: - break + if root and drv: break if drv or root: parsed.append(drv + root) @@ -115,6 +126,9 @@ def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): return drv, root, parts + parts2 return drv2, root2, parts2 + def has_drive(self, part): + return self.splitroot(part)[0] != '' + class _WindowsFlavour(_Flavour): # Reference for Windows paths can be found at @@ -191,11 +205,11 @@ def resolve(self, path, strict=False): s = str(path) if not s: return os.getcwd() - previous_s = None if _getfinalpathname is not None: if strict: return self._ext_to_normal(_getfinalpathname(s)) else: + previous_s = None tail_parts = [] # End of the path after the first one not found while True: try: @@ -203,6 +217,9 @@ def resolve(self, path, strict=False): except FileNotFoundError: previous_s = s s, tail = os.path.split(s) + if self.has_drive(tail): + # To avoid confusing between a filename with a data-stream and a drive letter + tail = f'.{self.sep}{tail}' tail_parts.append(tail) if previous_s == s: return path @@ -646,7 +663,10 @@ def _parse_args(cls, args): parts = [] for a in args: if isinstance(a, PurePath): - parts += a._parts + path_parts = a._parts + if a._drv or a._root: + path_parts = path_parts[1:] + parts.append((a._drv, a._root, path_parts)) else: a = os.fspath(a) if isinstance(a, str): @@ -684,6 +704,10 @@ def _from_parsed_parts(cls, drv, root, parts, init=True): @classmethod def _format_parsed_parts(cls, drv, root, parts): + if parts and not drv and cls._flavour.has_drive(parts[0]): + # In case there is no drive, and the first part might be interpreted as a drive, + # we add a dot to clarify the first part is not a drive. + parts = ['.'] + parts if drv or root: return drv + root + cls._flavour.join(parts[1:]) else: @@ -910,7 +934,7 @@ def __truediv__(self, key): return self._make_child((key,)) def __rtruediv__(self, key): - return self._from_parts([key] + self._parts) + return self._from_parts([key, self]) @property def parent(self): @@ -1138,7 +1162,7 @@ def absolute(self): return self # FIXME this must defer to the specific flavour (and, under Windows, # use nt._getfullpathname()) - obj = self._from_parts([os.getcwd()] + self._parts, init=False) + obj = self._from_parts([os.getcwd(), self], init=False) obj._init(template=self) return obj @@ -1507,7 +1531,7 @@ def expanduser(self): if (not (self._drv or self._root) and self._parts and self._parts[0][:1] == '~'): homedir = self._flavour.gethomedir(self._parts[0][1:]) - return self._from_parts([homedir] + self._parts[1:]) + return self._from_parts([homedir, self.relative_to(self._parts[0])]) return self diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 069467a8459f00..e6838ed067abe2 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -114,7 +114,6 @@ def test_parse_parts(self): check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) # Second part is anchored, so that the first part is ignored. - check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) # UNC paths. check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) @@ -133,6 +132,16 @@ def test_parse_parts(self): check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) + # Second part has a drive but not root. + check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'a', 'b', 'c'])) + check(['Y:a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'a', 'b', 'c'])) + # Paths to files with NTFS alternate data streams + check(['./c:s'], ('', '', ['c:s'])) + check(['cc:s'], ('', '', ['cc:s'])) + check(['C:c:s'], ('C:', '', ['C:', 'c:s'])) + check(['C:/c:s'], ('C:', '\\', ['C:\\', 'c:s'])) + check(['D:a', './c:b'], ('D:', '', ['D:', 'a', 'c:b'])) + check(['D:/a', './c:b'], ('D:', '\\', ['D:\\', 'a', 'c:b'])) def test_splitroot(self): f = self.flavour.splitroot @@ -201,6 +210,7 @@ def test_constructor_common(self): self.assertEqual(P(P('a'), 'b'), P('a/b')) self.assertEqual(P(P('a'), P('b')), P('a/b')) self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c"))) + self.assertEqual(P(P('./a:b')), P('./a:b')) def _check_str_subclass(self, *args): # Issue #21127: it should be possible to construct a PurePath object @@ -712,7 +722,9 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): equivalences = _BasePurePathTest.equivalences.copy() equivalences.update({ - 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ], + './a:b': [ ('./a:b',) ], + 'a:b:c': [ ('./b:c', 'a:'), ('b:', 'a:b:c') ], + 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('.', 'c:', 'a') ], 'c:/a': [ ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'), @@ -736,6 +748,7 @@ def test_str(self): self.assertEqual(str(p), '\\\\a\\b\\c\\d') def test_str_subclass(self): + self._check_str_subclass('.\\a:b') self._check_str_subclass('c:') self._check_str_subclass('c:a') self._check_str_subclass('c:a\\b.txt') @@ -882,6 +895,7 @@ def test_drive(self): self.assertEqual(P('//a/b').drive, '\\\\a\\b') self.assertEqual(P('//a/b/').drive, '\\\\a\\b') self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') + self.assertEqual(P('./c:a').drive, '') def test_root(self): P = self.cls @@ -1104,6 +1118,14 @@ def test_join(self): self.assertEqual(pp, P('C:/a/b/x/y')) pp = p.joinpath('c:/x/y') self.assertEqual(pp, P('C:/x/y')) + # Joining with files with NTFS data streams => the filename should + # not be parsed as a drive letter + pp = p.joinpath(P('./d:s')) + self.assertEqual(pp, P('C:/a/b/d:s')) + pp = p.joinpath(P('./dd:s')) + self.assertEqual(pp, P('C:/a/b/dd:s')) + pp = p.joinpath(P('E:d:s')) + self.assertEqual(pp, P('E:d:s')) def test_div(self): # Basically the same as joinpath(). @@ -1124,6 +1146,11 @@ def test_div(self): # the second path is relative. self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y')) self.assertEqual(p / 'c:/x/y', P('C:/x/y')) + # Joining with files with NTFS data streams => the filename should + # not be parsed as a drive letter + self.assertEqual(p / P('./d:s'), P('C:/a/b/d:s')) + self.assertEqual(p / P('./dd:s'), P('C:/a/b/dd:s')) + self.assertEqual(p / P('E:d:s'), P('E:d:s')) def test_is_reserved(self): P = self.cls @@ -1333,6 +1360,8 @@ def test_expanduser_common(self): self.assertEqual(p.expanduser(), p) p = P(P('').absolute().anchor) / '~' self.assertEqual(p.expanduser(), p) + p = P('~/a:b') + self.assertEqual(p.expanduser(), P(os.path.expanduser('~'), './a:b')) def test_exists(self): P = self.cls @@ -2328,6 +2357,11 @@ def check(): env['USERPROFILE'] = 'C:\\Users\\alice' check() + def test_resolve(self): + P = self.cls + p = P(BASE, './a:b') + self.assertEqual(str(p.resolve(strict=False)), f'{BASE}\\a:b') + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst b/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst new file mode 100644 index 00000000000000..9b280655aad7af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst @@ -0,0 +1 @@ +Fix a ``pathlib`` inconsistency in handling of paths containing colons. \ No newline at end of file From 86c04080887c1a557e37e011265413057697d313 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 22 Feb 2023 20:35:36 +0000 Subject: [PATCH 2/9] Undo pathlib changes --- Lib/pathlib.py | 80 ++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 6a24bba95a09ef..cfa574af6e8bab 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -60,49 +60,38 @@ class _Flavour(object): def __init__(self): self.join = self.sep.join - def _split_part(self, part): - """ - Return the drive, root and path parts from a given part. - If the part is a tuple, it already contains these values and therefore is returned. - Otherwise, splitroot is used to parse the part. - """ - if isinstance(part, tuple): - return part - elif isinstance(part, str): - if self.altsep: - part = part.replace(self.altsep, self.sep) - drv, root, rel = self.splitroot(part) - return drv, root, rel.split(self.sep) - else: - raise TypeError(f'argument should be a tuple or an str object, not {type(part)}') - def parse_parts(self, parts): - """ - Parse and join multiple path strings, and - return a tuple of the final drive, root and path parts. - The given parts can be either strings of paths, - or tuples that represent paths, containing the drive, root and list of path parts. - The option for passing a tuple is needed, as the part 'a:b' could be interpreted - either as the relative path 'b' with the drive 'a:', - or as a file 'a' with the NTFS data-stream 'b'. - For example, passing either ('a:', '', ['b']) or ('', '', ['a:b']) instead of 'a:b' - will allow parse_parts to behave properly in these cases. - """ parsed = [] + sep = self.sep + altsep = self.altsep drv = root = '' it = reversed(parts) for part in it: if not part: continue - current_drv, current_root, rel_parts = self._split_part(part) - if not drv: - drv = current_drv - if not root: - root = current_root - for x in reversed(rel_parts): + if altsep: + part = part.replace(altsep, sep) + drv, root, rel = self.splitroot(part) + if sep in rel: + for x in reversed(rel.split(sep)): if x and x != '.': parsed.append(sys.intern(x)) - if root and drv: + else: + if rel and rel != '.': + parsed.append(sys.intern(rel)) + if drv or root: + if not drv: + # If no drive is present, try to find one in the previous + # parts. This makes the result of parsing e.g. + # ("C:", "/", "a") reasonably intuitive. + for part in it: + if not part: + continue + if altsep: + part = part.replace(altsep, sep) + drv = self.splitroot(part)[0] + if drv: + break break if drv or root: parsed.append(drv + root) @@ -126,9 +115,6 @@ def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): return drv, root, parts + parts2 return drv2, root2, parts2 - def has_drive(self, part): - return self.splitroot(part)[0] != '' - class _WindowsFlavour(_Flavour): # Reference for Windows paths can be found at @@ -208,11 +194,11 @@ def resolve(self, path, strict=False): s = str(path) if not s: return os.getcwd() + previous_s = None if _getfinalpathname is not None: if strict: return self._ext_to_normal(_getfinalpathname(s)) else: - previous_s = None tail_parts = [] # End of the path after the first one not found while True: try: @@ -220,9 +206,6 @@ def resolve(self, path, strict=False): except FileNotFoundError: previous_s = s s, tail = os.path.split(s) - if self.has_drive(tail): - # To avoid confusing between a filename with a data-stream and a drive letter - tail = f'.{self.sep}{tail}' tail_parts.append(tail) if previous_s == s: return path @@ -670,10 +653,7 @@ def _parse_args(cls, args): parts = [] for a in args: if isinstance(a, PurePath): - path_parts = a._parts - if a._drv or a._root: - path_parts = path_parts[1:] - parts.append((a._drv, a._root, path_parts)) + parts += a._parts else: a = os.fspath(a) if isinstance(a, str): @@ -711,10 +691,6 @@ def _from_parsed_parts(cls, drv, root, parts, init=True): @classmethod def _format_parsed_parts(cls, drv, root, parts): - if parts and not drv and cls._flavour.has_drive(parts[0]): - # In case there is no drive, and the first part might be interpreted as a drive, - # we add a dot to clarify the first part is not a drive. - parts = ['.'] + parts if drv or root: return drv + root + cls._flavour.join(parts[1:]) else: @@ -965,7 +941,7 @@ def __truediv__(self, key): def __rtruediv__(self, key): try: - return self._from_parts([key, self]) + return self._from_parts([key] + self._parts) except TypeError: return NotImplemented @@ -1195,7 +1171,7 @@ def absolute(self): return self # FIXME this must defer to the specific flavour (and, under Windows, # use nt._getfullpathname()) - obj = self._from_parts([os.getcwd(), self], init=False) + obj = self._from_parts([os.getcwd()] + self._parts, init=False) obj._init(template=self) return obj @@ -1577,7 +1553,7 @@ def expanduser(self): if (not (self._drv or self._root) and self._parts and self._parts[0][:1] == '~'): homedir = self._flavour.gethomedir(self._parts[0][1:]) - return self._from_parts([homedir, self.relative_to(self._parts[0])]) + return self._from_parts([homedir] + self._parts[1:]) return self From cb9ce9df40d25ee6c2220de9d4ca008b5f126d88 Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 00:05:49 +0000 Subject: [PATCH 3/9] Remove a couple of incorrect test cases --- Lib/test/test_pathlib.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 30dab38cbc6720..f782a63ad59c55 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -121,9 +121,6 @@ def test_parse_parts(self): # the second path is relative. check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y'])) check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y'])) - # Second part has a drive but not root. - check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'a', 'b', 'c'])) - check(['Y:a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'a', 'b', 'c'])) # Paths to files with NTFS alternate data streams check(['./c:s'], ('', '', ['c:s'])) check(['cc:s'], ('', '', ['cc:s'])) @@ -825,7 +822,6 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): equivalences = _BasePurePathTest.equivalences.copy() equivalences.update({ './a:b': [ ('./a:b',) ], - 'a:b:c': [ ('./b:c', 'a:'), ('b:', 'a:b:c') ], 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('.', 'c:', 'a') ], 'c:/a': [ ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), From fe5cfb3c4bd04f2375742bc9d84933a902e2c732 Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 00:09:45 +0000 Subject: [PATCH 4/9] Fix `_format_parsed_parts()` --- Lib/pathlib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index d375529ff5f767..70896454970968 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -320,8 +320,9 @@ def _from_parsed_parts(cls, drv, root, parts): def _format_parsed_parts(cls, drv, root, parts): if drv or root: return drv + root + cls._flavour.sep.join(parts[1:]) - else: - return cls._flavour.sep.join(parts) + elif parts and cls._flavour.splitdrive(parts[0])[0]: + parts = ['.'] + parts + return cls._flavour.sep.join(parts) def __str__(self): """Return the string representation of the path, suitable for From bc225f572eb1c8b4099f62081b92ca08d262848d Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 00:28:11 +0000 Subject: [PATCH 5/9] Restore deleted test case. --- Lib/test/test_pathlib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index f782a63ad59c55..85855ab5c92c30 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -99,6 +99,7 @@ def test_parse_parts(self): check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) # Second part is anchored, so that the first part is ignored. + check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) # UNC paths. check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) From 9aa40770de83f7fa78197da9fb46dc7318fab9b1 Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 02:16:50 +0000 Subject: [PATCH 6/9] Fix expanduser() --- Lib/pathlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 70896454970968..680795dbc5be7c 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1186,10 +1186,10 @@ def expanduser(self): """ if (not (self._drv or self._root) and self._parts and self._parts[0][:1] == '~'): - homedir = self._flavour.expanduser(self._parts[0]) + homedir = self._flavour.expanduser(self) if homedir[:1] == "~": raise RuntimeError("Could not determine home directory.") - return self._from_parts([homedir] + self._parts[1:]) + return self._from_parts([homedir]) return self From 0be2dc417866786891dffb503112cd1a11877c34 Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 16:40:23 +0000 Subject: [PATCH 7/9] Use `_from_parsed_parts()` in `expanduser()` --- Lib/pathlib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 680795dbc5be7c..55c44f12e5a2fb 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1186,10 +1186,11 @@ def expanduser(self): """ if (not (self._drv or self._root) and self._parts and self._parts[0][:1] == '~'): - homedir = self._flavour.expanduser(self) + homedir = self._flavour.expanduser(self._parts[0]) if homedir[:1] == "~": raise RuntimeError("Could not determine home directory.") - return self._from_parts([homedir]) + drv, root, parts = self._parse_parts((homedir,)) + return self._from_parsed_parts(drv, root, parts + self._parts[1:]) return self From 8386140eeef31d2902879308fc6a2a05beb9cd0a Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 18:18:10 +0000 Subject: [PATCH 8/9] Improve news blurb. --- .../next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst b/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst index 9b280655aad7af..d9360496ac24cb 100644 --- a/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst +++ b/Misc/NEWS.d/next/Library/2019-03-15-22-50-27.bpo-36305.Pbkv6u.rst @@ -1 +1,2 @@ -Fix a ``pathlib`` inconsistency in handling of paths containing colons. \ No newline at end of file +Fix handling of Windows filenames that resemble drives, such as ``./a:b``, +in :mod:`pathlib`. From e6f11d45ac990022591d53b4f6c7bfe5b7e74bf7 Mon Sep 17 00:00:00 2001 From: barneygale Date: Mon, 6 Mar 2023 18:18:44 +0000 Subject: [PATCH 9/9] Remove test case affected by GH-102475. --- Lib/test/test_pathlib.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 85855ab5c92c30..6bd1e7a540d730 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -3116,11 +3116,6 @@ def check(): env['HOME'] = 'C:\\Users\\eve' check() - def test_resolve(self): - P = self.cls - p = P(BASE, './a:b') - self.assertEqual(str(p.resolve(strict=False)), f'{BASE}\\a:b') - class PurePathSubclassTest(_BasePurePathTest, unittest.TestCase): class cls(pathlib.PurePath):