Issue #27186: Add os.PathLike support to pathlib.

This adds support both to pathlib.PurePath's constructor as well as
implementing __fspath__(). This removes the provisional status for
pathlib.

Initial patch by Dusty Phillips.
This commit is contained in:
Brett Cannon 2016-06-10 12:20:49 -07:00
parent f41b82fb19
commit 568be63248
4 changed files with 56 additions and 14 deletions

View File

@ -31,12 +31,6 @@ Pure paths are useful in some special cases; for example:
accessing the OS. In this case, instantiating one of the pure classes may be
useful since those simply don't have any OS-accessing operations.
.. note::
This module has been included in the standard library on a
:term:`provisional basis <provisional package>`. Backwards incompatible
changes (up to and including removal of the package) may occur if deemed
necessary by the core developers.
.. seealso::
:pep:`428`: The pathlib module -- object-oriented filesystem paths.
@ -107,7 +101,8 @@ we also call *flavours*:
PurePosixPath('setup.py')
Each element of *pathsegments* can be either a string representing a
path segment, or another path object::
path segment, an object implementing the :class:`os.PathLike` interface
which returns a string, or another path object::
>>> PurePath('foo', 'some/path', 'bar')
PurePosixPath('foo/some/path/bar')
@ -148,6 +143,12 @@ we also call *flavours*:
to ``PurePosixPath('bar')``, which is wrong if ``foo`` is a symbolic link
to another directory)
Pure path objects implement the :class:`os.PathLike` interface, allowing them
to be used anywhere the interface is accepted.
.. versionchanged:: 3.6
Added support for the :class:`os.PathLike` interface.
.. class:: PurePosixPath(*pathsegments)
A subclass of :class:`PurePath`, this path flavour represents non-Windows
@ -212,6 +213,14 @@ The slash operator helps create child paths, similarly to :func:`os.path.join`::
>>> '/usr' / q
PurePosixPath('/usr/bin')
A path object can be used anywhere an object implementing :class:`os.PathLike`
is accepted::
>>> import os
>>> p = PurePath('/etc')
>>> os.fspath(p)
'/etc'
The string representation of a path is the raw filesystem path itself
(in native form, e.g. with backslashes under Windows), which you can
pass to any function taking a file path as a string::

View File

@ -634,13 +634,16 @@ class PurePath(object):
for a in args:
if isinstance(a, PurePath):
parts += a._parts
elif isinstance(a, str):
# Force-cast str subclasses to str (issue #21127)
parts.append(str(a))
else:
raise TypeError(
"argument should be a path or str object, not %r"
% type(a))
a = os.fspath(a)
if isinstance(a, str):
# Force-cast str subclasses to str (issue #21127)
parts.append(str(a))
else:
raise TypeError(
"argument should be a str object or an os.PathLike "
"object returning str, not %r"
% type(a))
return cls._flavour.parse_parts(parts)
@classmethod
@ -693,6 +696,9 @@ class PurePath(object):
self._parts) or '.'
return self._str
def __fspath__(self):
return str(self)
def as_posix(self):
"""Return the string representation of the path with forward (/)
slashes."""
@ -943,6 +949,10 @@ class PurePath(object):
return False
return True
# Can't subclass os.PathLike from PurePath and keep the constructor
# optimizations in PurePath._parse_args().
os.PathLike.register(PurePath)
class PurePosixPath(PurePath):
_flavour = _posix_flavour

View File

@ -190,13 +190,18 @@ class _BasePurePathTest(object):
P = self.cls
p = P('a')
self.assertIsInstance(p, P)
class PathLike:
def __fspath__(self):
return "a/b/c"
P('a', 'b', 'c')
P('/a', 'b', 'c')
P('a/b/c')
P('/a/b/c')
P(PathLike())
self.assertEqual(P(P('a')), P('a'))
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(PathLike()))
def _check_str_subclass(self, *args):
# Issue #21127: it should be possible to construct a PurePath object
@ -384,6 +389,12 @@ class _BasePurePathTest(object):
parts = p.parts
self.assertEqual(parts, (sep, 'a', 'b'))
def test_fspath_common(self):
P = self.cls
p = P('a/b')
self._check_str(p.__fspath__(), ('a/b',))
self._check_str(os.fspath(p), ('a/b',))
def test_equivalences(self):
for k, tuples in self.equivalences.items():
canon = k.replace('/', self.sep)

View File

@ -10,6 +10,8 @@ What's New in Python 3.6.0 alpha 2
Core and Builtins
-----------------
- Issue #27186: Add support for os.PathLike objects to open() (part of PEP 519).
- Issue #27066: Fixed SystemError if a custom opener (for open()) returns a
negative number without setting an exception.
@ -36,6 +38,14 @@ Core and Builtins
Library
-------
- Issue #27186: Add os.PathLike support to pathlib, removing its provisional
status (part of PEP 519).
- Issue #27186: Add support for os.PathLike objects to os.fsencode() and
os.fsdecode() (part of PEP 519).
- Issue #27186: Introduce os.PathLike and os.fspath() (part of PEP 519).
- A new version of typing.py provides several new classes and
features: @overload outside stubs, Reversible, DefaultDict, Text,
ContextManager, Type[], NewType(), TYPE_CHECKING, and numerous bug
@ -198,12 +208,14 @@ Build
Misc
----
- Issue #17500, and https://github.com/python/pythondotorg/issues/945: Remove
- Issue #17500, and https://github.com/python/pythondotorg/issues/945: Remove
unused and outdated icons.
C API
-----
- Issue #27186: Add the PyOS_FSPath() function (part of PEP 519).
- Issue #26282: PyArg_ParseTupleAndKeywords() now supports positional-only
parameters.