Antoine Pitrou avatar Antoine Pitrou committed 4f80166

Add PurePath.as_uri().

Comments (0)

Files changed (3)

       'c:/windows'
 
 
+.. method:: PurePath.as_uri()
+
+   Represent the path as a ``file`` URI.  :exc:`ValueError` is raised if
+   the path isn't absolute.
+
+      >>> p = PurePosixPath('/etc/passwd')
+      >>> p.as_uri()
+      'file:///etc/passwd'
+      >>> p = PureNTPath('c:/Windows')
+      >>> p.as_uri()
+      'file:///c:/Windows'
+
+
 .. method:: PurePath.is_absolute()
 
    Return whether the path is absolute or not.  A path is considered absolute
 from itertools import chain, count
 from operator import attrgetter
 from stat import S_ISDIR, S_ISLNK, S_ISREG
+try:
+    from urllib import quote as urlquote, quote as urlquote_from_bytes
+except ImportError:
+    from urllib.parse import quote as urlquote, quote_from_bytes as urlquote_from_bytes
 
 
 supports_symlinks = True
             return False
         return parts[-1].partition('.')[0].upper() in self.reserved_names
 
+    def make_uri(self, path):
+        # Under Windows, file URIs use the UTF-8 encoding.
+        drive = path.drive
+        if len(drive) == 2 and drive[1] == ':':
+            # It's a path on a local drive => 'file:///c:/a/b'
+            rest = path.as_posix()[2:].lstrip('/')
+            return 'file:///%s/%s' % (
+                drive, urlquote_from_bytes(rest.encode('utf-8')))
+        else:
+            # It's a path on a network drive => 'file://host/share/a/b'
+            return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
+
 
 _NO_FD = None
 
     def is_reserved(self, parts):
         return False
 
+    def make_uri(self, path):
+        # We represent the path using the local filesystem encoding,
+        # for portability to other applications.
+        bpath = path.as_bytes()
+        return 'file://' + urlquote_from_bytes(bpath)
+
 
 _nt_flavour = _NTFlavour()
 _posix_flavour = _PosixFlavour()
     def as_bytes(self):
         """Return the bytes representation of the path.  This is only
         recommended to use under Unix."""
+        if sys.version_info < (3, 2):
+            raise NotImplementedError("needs Python 3.2 or later")
         return os.fsencode(str(self))
 
     __bytes__ = as_bytes
     def __repr__(self):
         return "{}({!r})".format(self.__class__.__name__, str(self))
 
+    def as_uri(self):
+        """Return the path as a 'file' URI."""
+        if not self.is_absolute():
+            raise ValueError("relative path can't be expressed as a file URI")
+        return self._flavour.make_uri(self)
+
     @property
     def _cparts(self):
         # Cached casefolded parts, for hashing and comparison
 # Tests for the pure classes
 #
 
+with_fsencode = unittest.skipIf(sys.version_info < (3, 2),
+    'os.fsencode has been introduced in version 3.2')
+
 class _BasePurePathTest(unittest.TestCase):
 
     # keys are canonical paths, values are list of tuples of arguments
             self.assertEqual(P(pathstr).as_posix(), pathstr)
         # Other tests for as_posix() are in test_equivalences()
 
-    @unittest.skipIf(sys.version_info < (3, 2),
-                     'os.fsencode has been introduced in version 3.2')
+    @with_fsencode
     def test_as_bytes_common(self):
         sep = os.fsencode(self.sep)
         P = self.cls
         self.assertEqual(P('a/b').as_bytes(), b'a' + sep + b'b')
         self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b')
 
+    @with_fsencode
+    def test_as_uri_common(self):
+        P = self.cls
+        with self.assertRaises(ValueError):
+            P('a').as_uri()
+        with self.assertRaises(ValueError):
+            P().as_uri()
+
     def test_repr_common(self):
         for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
             p = self.cls(pathstr)
         self.assertEqual(P('/a'), P('///a'))
         self.assertNotEqual(P('/a'), P('//a'))
 
+    @with_fsencode
+    def test_as_uri(self):
+        from urllib.parse import quote_from_bytes
+        P = self.cls
+        self.assertEqual(P('/').as_uri(), 'file:///')
+        self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c')
+        self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c')
+        self.assertEqual(P('/a/b\xe9').as_uri(),
+                         'file:///a/b' + quote_from_bytes(os.fsencode('\xe9')))
+
     def test_match(self):
         P = self.cls
         self.assertFalse(P('A.py').match('a.PY'))
         self.assertEqual(P('C:a/B'), P('c:A/b'))
         self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b'))
 
+    @with_fsencode
+    def test_as_uri(self):
+        from urllib.parse import quote_from_bytes
+        P = self.cls
+        with self.assertRaises(ValueError):
+            P('/a/b').as_uri()
+        with self.assertRaises(ValueError):
+            P('c:a/b').as_uri()
+        self.assertEqual(P('c:/').as_uri(), 'file:///c:/')
+        self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c')
+        self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c')
+        self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9')
+        self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/')
+        self.assertEqual(P('//some/share/a/b.c').as_uri(),
+                         'file://some/share/a/b.c')
+        self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
+                         'file://some/share/a/b%25%23c%C3%A9')
+
     def test_match_common(self):
         P = self.cls
         # Absolute patterns
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.