]> granicus.if.org Git - python/commitdiff
Issue #27186: Add os.PathLike support to pathlib.
authorBrett Cannon <brett@python.org>
Fri, 10 Jun 2016 19:20:49 +0000 (12:20 -0700)
committerBrett Cannon <brett@python.org>
Fri, 10 Jun 2016 19:20:49 +0000 (12:20 -0700)
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.

Doc/library/pathlib.rst
Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS

index ff5196de86002fbcf81ef75963ae013a5d1d91f0..ca44a6518814ff8ea8133a16091f319cd69c3085 100644 (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::
index 1480e2fc71aaac4b9c54b862c6cfb945f249ea08..a06676fbe4f239cd0166bbbae56fc1b01a723856 100644 (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
index fbbd448f65dd8593a55c811dd3321daad7dd5dbf..2f2ba3cbfc7c352fcd35d193e89b1f8635380829 100644 (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)
index f382b3f29f395f0f95dc09c0a7370debdb88480e..8eaed2359e7db6b1a6ca0b9a88d3d26a9db42aa9 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.