Skip to content
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

GH-106037: Disarm os.PathLike foot-shotgun in pathlib.PurePath user subclasses #106043

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 17 additions & 9 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,6 @@ def __str__(self):
self._tail) or '.'
return self._str

def __fspath__(self):
return str(self)

def as_posix(self):
"""Return the string representation of the path with forward (/)
slashes."""
Expand All @@ -411,7 +408,7 @@ def as_posix(self):
def __bytes__(self):
"""Return the bytes representation of the path. This is only
recommended to use under Unix."""
return os.fsencode(self)
return os.fsencode(str(self))

def __repr__(self):
return "{}({!r})".format(self.__class__.__name__, self.as_posix())
Expand Down Expand Up @@ -743,11 +740,6 @@ def match(self, path_pattern, *, case_sensitive=None):
raise ValueError("empty pattern")


# Subclassing os.PathLike makes isinstance() checks slower,
# which in turn makes Path construction slower. Register instead!
os.PathLike.register(PurePath)


class PurePosixPath(PurePath):
"""PurePath subclass for non-Windows systems.

Expand All @@ -757,6 +749,8 @@ class PurePosixPath(PurePath):
_flavour = posixpath
__slots__ = ()

def __fspath__(self):
return str(self)

class PureWindowsPath(PurePath):
"""PurePath subclass for Windows systems.
Expand All @@ -767,6 +761,13 @@ class PureWindowsPath(PurePath):
_flavour = ntpath
__slots__ = ()

def __fspath__(self):
return str(self)


# These classes implement os.PathLike for backwards compatibility.
os.PathLike.register(PurePosixPath)
os.PathLike.register(PureWindowsPath)

# Filesystem-accessing classes

Expand Down Expand Up @@ -1174,6 +1175,9 @@ def __new__(cls, *args, **kwargs):
cls = WindowsPath if os.name == 'nt' else PosixPath
return object.__new__(cls)

def __fspath__(self):
return str(self)

@classmethod
def cwd(cls):
"""Return a new path pointing to the current working directory."""
Expand Down Expand Up @@ -1398,6 +1402,10 @@ def expanduser(self):

return self

# Subclassing os.PathLike makes isinstance() checks slower,
# which in turn makes Path construction slower. Register instead!
os.PathLike.register(Path)


class PosixPath(Path, PurePosixPath):
"""Path subclass for non-Windows systems.
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,15 @@ class cls(pathlib.PurePath):
# repr() roundtripping is not supported in custom subclass.
test_repr_roundtrips = None

# PurePath subclass is not automatically path-like
def test_fspath_common(self):
P = self.cls
p = P()
self.assertFalse(issubclass(P, os.PathLike))
self.assertFalse(isinstance(p, os.PathLike))
with self.assertRaises(TypeError):
os.fspath(p)


@only_posix
class PosixPathAsPureTest(PurePosixPathTest):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix misidentification of :class:`pathlib.PurePath` user subclasses as
:class:`os.PathLike`. Subclasses are free to define an
:meth:`~os.PathLike.__fspath__` method, but do not inherit one.