8000 gh-68320, gh-88302 - Allow for `pathlib.Path` subclassing by barneygale · Pull Request #31691 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-68320, gh-88302 - Allow for pathlib.Path subclassing #31691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 53 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7371220
bpo-24132, bpo-44136 - Fix `pathlib.Path` subclassing.
barneygale Mar 5, 2022
8431d12
Add tests
barneygale Mar 5, 2022
bf2ad3a
Add tests for PurePath subclasses
barneygale Mar 5, 2022
4036e2e
Update Misc/NEWS.d/next/Library/2022-03-05-02-14-09.bpo-24132.W6iORO.rst
barneygale Mar 5, 2022
0d08533
Rename `PurePath._pathmod` to `PurePath._flavour` to reduce the diff.
barneygale Mar 5, 2022
0ad8022
Merge branch 'bpo-44136-remove-pathlib-flavour-2' of github.com:barne…
barneygale Mar 5, 2022
28992fc
Undo an unnecessary change to `Path.__new__()`
barneygale Mar 25, 2022
dbec230
Remove `_casefold()` and `_casefold_parts()` methods.
barneygale Mar 25, 2022
d814ee4
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Apr 29, 2022
63f1d68
Further simplify overall diff by inlining code from `compile_pattern()`
barneygale Apr 29, 2022
8dddfdf
Apply suggestions from code review
barneygale Apr 29, 2022
b4da721
Address a couple bits of review feedback
barneygale Apr 29, 2022
7a166fd
Fix tests
barneygale Apr 29, 2022
1f4dff1
Fix docstring formatting.
barneygale Apr 29, 2022
51893c1
Remove FIXME comments
barneygale Apr 30, 2022
48b49b6
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Apr 30, 2022
3624c97
Update Lib/pathlib.py
barneygale May 3, 2022
ab54c14
Remove vestigal 'casefold' names.
barneygale May 31, 2022
2627417
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Jun 10, 2022
bc7aded
Restore comment on POSIX paths beginning `//` and tweak implementation.
barneygale Jun 10, 2022
4d6e4f3
Inline `_join_parsed_parts()` and `_make_child()` in `joinpath()`.
barneygale Jun 12, 2022
70ca838
Update Lib/pathlib.py
barneygale Jun 13, 2022
a2cc74f
Remove unused import
barneygale Jun 16, 2022
0e2478b
Make NEWS entry more precise.
barneygale Jun 17, 2022
0d0cf60
Apply suggestions from code review
barneygale Jul 29, 2022
f3048d3
Clarify `_pparts` and `_ncparts` naming.
barneygale Jul 29, 2022
8a213ae
Tidy up usage of `normcase()`
barneygale Jul 30, 2022
402dafe
Tweak `is_absolute()` to use `os.path.isabs()` wherever possible.
barneygale Jul 30, 2022
5cc3ab2
Stop using `self._flavour` in `as_uri()`.
barneygale Jul 30, 2022
19a8804
Remove `cls._flavour` identity check in `_parse_parts()`.
barneygale Jul 30, 2022
9da46bc
Fix Windows tests
barneygale Jul 30, 2022
29f9f81
Restore `_split_root()` method and its tests.
barneygale Aug 1, 2022
52851b8
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Aug 5, 2022
b39da53
Optimize `_split_root()`
barneygale Aug 12, 2022
c7b18b1
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Aug 13, 2022
806ad15
Move `is_reserved()` implementation into `os.path` as a private funct…
barneygale Aug 23, 2022
a350009
Use `os.path.normcase()` when globbing.
barneygale Aug 23, 2022
799b40e
Simplify `_split_root()` implementation.
barneygale Aug 25, 2022
a6b25ea
Remove tests for malformed UNC paths
barneygale Aug 26, 2022
ac6f6e2
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
brettcannon Aug 26, 2022
b8874df
Revert "Move `is_reserved()` implementation into `os.path` as a priva…
barneygale Aug 27, 2022
9d5e18a
Tweak _split_root() to use removesuffix()
barneygale Aug 27, 2022
f73f426
Remove test that `/b/c/d` and `///b/c/d` are equivalent.
barneygale Aug 27, 2022
37e4bc1
Restore comment linking to *Naming Files* page in Microsoft docs
barneygale Sep 16, 2022
e3f13ef
Apply suggestions from code review
barneygale Nov 7, 2022
ff4e1b3
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Nov 15, 2022
07c67d0
Clarify repr roundtrip tests.
barneygale Nov 15, 2022
950aa47
Reduce tests diff
barneygale Nov 15, 2022
d5f6f03
Remove test that `/b/c/d` and `///b/c/d` are equivalent.
barneygale Aug 27, 2022
b66a4fb
Fix missing full stops.
barneygale Nov 15, 2022
fbaadf4
Remove four more test assertions affected by #96290
barneygale Nov 15, 2022
3692fdf
Merge branch 'main' into bpo-44136-remove-pathlib-flavour-2
barneygale Nov 25, 2022
40dc514
Wrap repr test cases in `self.subTest()` block
barneygale Dec 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rename PurePath._pathmod to PurePath._flavour to reduce the diff.
  • Loading branch information
barneygale committed Mar 5, 2022
commit 0d08533ca9c4938873ff826ea098cacef0d2374d
60 changes: 30 additions & 30 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ class PurePath(object):
'_drv', '_root', '_parts',
'_str', '_hash', '_pparts', '_cached_cparts',
)
_pathmod = os.path
_flavour = os.path

def __new__(cls, *args):
"""Construct a PurePath from one or several strings and or existing
Expand All @@ -259,15 +259,15 @@ def __reduce__(self):

@classmethod
def _casefold(cls, s):
return cls._pathmod.normcase(s)
return cls._flavour.normcase(s)

@classmethod
def _casefold_parts(cls, parts):
return [cls._pathmod.normcase(p) for p in parts]
return [cls._flavour.normcase(p) for p in parts]

@classmethod
def _compile_pattern(cls, pattern):
flags = 0 if cls._pathmod is posixpath else re.IGNORECASE
flags = 0 if cls._flavour is posixpath else re.IGNORECASE
return re.compile(fnmatch.translate(pattern), flags).fullmatch

@classmethod
Expand All @@ -284,8 +284,8 @@ def _split_extended_path(cls, s):
@classmethod
def _splitroot(cls, part):
# FIXME: This method should use os.path.splitdrive()
sep = cls._pathmod.sep
if cls._pathmod is posixpath:
sep = cls._flavour.sep
if cls._flavour is posixpath:
if part and part[0] == sep:
stripped_part = part.lstrip(sep)
# According to POSIX path resolution:
Expand Down Expand Up @@ -341,8 +341,8 @@ def _splitroot(cls, part):
@classmethod
def _parse_parts(cls, parts):
parsed = []
sep = cls._pathmod.sep
altsep = cls._pathmod.altsep
sep = cls._flavour.sep
altsep = cls._flavour.altsep
drv = root = ''
it = reversed(parts)
for part in it:
Expand Down Expand Up @@ -419,9 +419,9 @@ def _from_parsed_parts(cls, drv, root, parts):
@classmethod
def _format_parsed_parts(cls, drv, root, parts):
if drv or root:
return drv + root + cls._pathmod.sep.join(parts[1:])
return drv + root + cls._flavour.sep.join(parts[1:])
else:
return cls._pathmod.sep.join(parts)
return cls._flavour.sep.join(parts)

def _join_parsed_parts(self, drv2, root2, parts2):
"""
Expand Down Expand Up @@ -462,8 +462,8 @@ def __fspath__(self):
def as_posix(self):
"""Return the string representation of the path with forward (/)
slashes."""
m = self._pathmod
return str(self).replace(m.sep, '/')
f = self._flavour
return str(self).replace(f.sep, '/')

def __bytes__(self):
"""Return the bytes representation of the path. This is only
Expand All @@ -479,7 +479,7 @@ def as_uri(self):
raise ValueError("relative path can't be expressed as a file URI")

# FIXME: move this implementation to os.path.fileuri()
if self._pathmod is posixpath:
if self._flavour is posixpath:
# On POSIX we represent the path using the local filesystem encoding,
# for portability to other applications.
return 'file://' + urlquote_from_bytes(bytes(self))
Expand Down Expand Up @@ -507,7 +507,7 @@ def _cparts(self):
def __eq__(self, other):
if not isinstance(other, PurePath):
return NotImplemented
return self._cparts == other._cparts and self._pathmod is other._pathmod
return self._cparts == other._cparts and self._flavour is other._flavour

def __hash__(self):
try:
Expand All @@ -517,22 +517,22 @@ def __hash__(self):
return self._hash

def __lt__(self, other):
if not isinstance(other, PurePath) or self._pathmod is not other._pathmod:
if not isinstance(other, PurePath) or self._flavour is not other._flavour:
return NotImplemented
return self._cparts < other._cparts

def __le__(self, other):
if not isinstance(other, PurePath) or self._pathmod is not other._pathmod:
if not isinstance(other, PurePath) or self._flavour is not other._flavour:
return NotImplemented
return self._cparts <= other._cparts

def __gt__(self, other):
if not isinstance(other, PurePath) or self._pathmod is not other._pathmod:
if not isinstance(other, PurePath) or self._flavour is not other._flavour:
return NotImplemented
return self._cparts > other._cparts

def __ge__(self, other):
if not isinstance(other, PurePath) or self._pathmod is not other._pathmod:
if not isinstance(other, PurePath) or self._flavour is not other._flavour:
return NotImplemented
return self._cparts >= other._cparts

Expand Down Expand Up @@ -598,7 +598,7 @@ def with_name(self, name):
if not self.name:
raise ValueError("%r has an empty name" % (self,))
drv, root, parts = self._parse_parts((name,))
if (not name or name[-1] in [self._pathmod.sep, self._pathmod.altsep]
if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
or drv or root or len(parts) != 1):
raise ValueError("Invalid name %r" % (name))
return self._from_parsed_parts(self._drv, self._root,
Expand All @@ -613,8 +613,8 @@ def with_suffix(self, suffix):
has no suffix, add given suffix. If the given suffix is an empty
string, remove the suffix from the path.
"""
m = self._pathmod
if m.sep in suffix or m.altsep and m.altsep in suffix:
f = self._flavour
if f.sep in suffix or f.altsep and f.altsep in suffix:
raise ValueError("Invalid suffix %r" % (suffix,))
if suffix and not suffix.startswith('.') or suffix == '.':
raise ValueError("Invalid suffix %r" % (suffix))
Expand Down Expand Up @@ -723,13 +723,13 @@ def is_absolute(self):
a drive)."""
if not self._root:
return False
return self._pathmod is posixpath or bool(self._drv)
return self._flavour is posixpath or bool(self._drv)

def is_reserved(self):
"""Return True if the path contains one of the special names reserved
by the system, if any."""
# FIXME: move this implementation to os.path.isreserved()
if self._pathmod is posixpath or not self._parts:
if self._flavour is posixpath or not self._parts:
return False

# NOTE: the rules for reserved names seem somewhat complicated
Expand Down Expand Up @@ -778,7 +778,7 @@ class PurePosixPath(PurePath):
On a POSIX system, instantiating a PurePath should return this object.
However, you can also instantiate it directly on any system.
"""
_pathmod = posixpath
_flavour = posixpath
__slots__ = ()


Expand All @@ -788,7 +788,7 @@ class PureWindowsPath(PurePath):
On a Windows system, instantiating a PurePath should return this object.
However, you can also instantiate it directly on any system.
"""
_pathmod = ntpath
_flavour = ntpath
__slots__ = ()


Expand All @@ -809,7 +809,7 @@ class Path(PurePath):
def __new__(cls, *args, **kwargs):
A3E2 if cls is Path:
cls = WindowsPath if os.name == 'nt' else PosixPath
elif cls._pathmod is not os.path:
elif cls._flavour is not os.path:
raise NotImplementedError("cannot instantiate %r on your system"
% (cls.__name__,))
return cls._from_parts(args)
Expand Down Expand Up @@ -862,7 +862,7 @@ def samefile(self, other_path):
other_st = other_path.stat()
except AttributeError:
other_st = self.__class__(other_path).stat()
return self._pathmod.samestat(st, other_st)
return self._flavour.samestat(st, other_st)

def iterdir(self):
"""Iterate over the files in this directory. Does not yield any
Expand Down Expand Up @@ -926,7 +926,7 @@ def check_eloop(e):
raise RuntimeError("Symlink loop from %r" % e.filename)

try:
s = self._pathmod.realpath(self, strict=strict)
s = self._flavour.realpath(self, strict=strict)
except OSError as e:
check_eloop(e)
raise
Expand Down Expand Up @@ -1215,7 +1215,7 @@ def is_mount(self):
"""
Check if this path is a POSIX mount point
"""
if self._pathmod is not posixpath:
if self._flavour is not posixpath:
raise NotImplementedError("Path.is_mount() is unsupported on this system")

# Need to exist and be a dir
Expand Down Expand Up @@ -1319,7 +1319,7 @@ def expanduser(self):
"""
if (not (self._drv or self._root) and
self._parts and self._parts[0][:1] == '~'):
homedir = self._pathmod.expanduser(self._parts[0])
homedir = self._flavour.expanduser(self._parts[0])
if homedir[:1] == "~":
raise RuntimeError("Could not determine home directory.")
return self._from_parts([homedir] + self._parts[1:])
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class _BasePurePathTest(object):
}

def setUp(self):
self.sep = self.cls._pathmod.sep
self.altsep = self.cls._pathmod.altsep
self.sep = self.cls._flavour.sep
self.altsep = self.cls._flavour.altsep

def _check_parse_parts(self, arg, expected):
f = self.cls._parse_parts
Expand Down
0