--- /dev/null
+import unittest
+
+from test.support import warnings_helper
+
+
+nturl2path = warnings_helper.import_deprecated("nturl2path")
+
+
+class NTURL2PathTest(unittest.TestCase):
+ """Test pathname2url() and url2pathname()"""
+
+ def test_basic(self):
+ # Make sure simple tests pass
+ expected_path = r"parts\of\a\path"
+ expected_url = "parts/of/a/path"
+ result = nturl2path.pathname2url(expected_path)
+ self.assertEqual(expected_url, result,
+ "pathname2url() failed; %s != %s" %
+ (result, expected_url))
+ result = nturl2path.url2pathname(expected_url)
+ self.assertEqual(expected_path, result,
+ "url2pathame() failed; %s != %s" %
+ (result, expected_path))
+
+ def test_pathname2url(self):
+ # Test special prefixes are correctly handled in pathname2url()
+ fn = nturl2path.pathname2url
+ self.assertEqual(fn('\\\\?\\C:\\dir'), '///C:/dir')
+ self.assertEqual(fn('\\\\?\\unc\\server\\share\\dir'), '//server/share/dir')
+ self.assertEqual(fn("C:"), '///C:')
+ self.assertEqual(fn("C:\\"), '///C:/')
+ self.assertEqual(fn('c:\\a\\b.c'), '///c:/a/b.c')
+ self.assertEqual(fn('C:\\a\\b.c'), '///C:/a/b.c')
+ self.assertEqual(fn('C:\\a\\b.c\\'), '///C:/a/b.c/')
+ self.assertEqual(fn('C:\\a\\\\b.c'), '///C:/a//b.c')
+ self.assertEqual(fn('C:\\a\\b%#c'), '///C:/a/b%25%23c')
+ self.assertEqual(fn('C:\\a\\b\xe9'), '///C:/a/b%C3%A9')
+ self.assertEqual(fn('C:\\foo\\bar\\spam.foo'), "///C:/foo/bar/spam.foo")
+ # NTFS alternate data streams
+ self.assertEqual(fn('C:\\foo:bar'), '///C:/foo%3Abar')
+ self.assertEqual(fn('foo:bar'), 'foo%3Abar')
+ # No drive letter
+ self.assertEqual(fn("\\folder\\test\\"), '///folder/test/')
+ self.assertEqual(fn("\\\\folder\\test\\"), '//folder/test/')
+ self.assertEqual(fn("\\\\\\folder\\test\\"), '///folder/test/')
+ self.assertEqual(fn('\\\\some\\share\\'), '//some/share/')
+ self.assertEqual(fn('\\\\some\\share\\a\\b.c'), '//some/share/a/b.c')
+ self.assertEqual(fn('\\\\some\\share\\a\\b%#c\xe9'), '//some/share/a/b%25%23c%C3%A9')
+ # Alternate path separator
+ self.assertEqual(fn('C:/a/b.c'), '///C:/a/b.c')
+ self.assertEqual(fn('//some/share/a/b.c'), '//some/share/a/b.c')
+ self.assertEqual(fn('//?/C:/dir'), '///C:/dir')
+ self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir')
+ # Round-tripping
+ urls = ['///C:',
+ '///folder/test/',
+ '///C:/foo/bar/spam.foo']
+ for url in urls:
+ self.assertEqual(fn(nturl2path.url2pathname(url)), url)
+
+ def test_url2pathname(self):
+ fn = nturl2path.url2pathname
+ self.assertEqual(fn('/'), '\\')
+ self.assertEqual(fn('/C:/'), 'C:\\')
+ self.assertEqual(fn("///C|"), 'C:')
+ self.assertEqual(fn("///C:"), 'C:')
+ self.assertEqual(fn('///C:/'), 'C:\\')
+ self.assertEqual(fn('/C|//'), 'C:\\\\')
+ self.assertEqual(fn('///C|/path'), 'C:\\path')
+ # No DOS drive
+ self.assertEqual(fn("///C/test/"), '\\C\\test\\')
+ self.assertEqual(fn("////C/test/"), '\\\\C\\test\\')
+ # DOS drive paths
+ self.assertEqual(fn('c:/path/to/file'), 'c:\\path\\to\\file')
+ self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn('C:/path/to/file/'), 'C:\\path\\to\\file\\')
+ self.assertEqual(fn('C:/path/to//file'), 'C:\\path\\to\\\\file')
+ self.assertEqual(fn('C|/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn('/C|/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn('///C|/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn("///C|/foo/bar/spam.foo"), 'C:\\foo\\bar\\spam.foo')
+ # Colons in URI
+ self.assertEqual(fn('///\u00e8|/'), '\u00e8:\\')
+ self.assertEqual(fn('//host/share/spam.txt:eggs'), '\\\\host\\share\\spam.txt:eggs')
+ self.assertEqual(fn('///c:/spam.txt:eggs'), 'c:\\spam.txt:eggs')
+ # UNC paths
+ self.assertEqual(fn('//server/path/to/file'), '\\\\server\\path\\to\\file')
+ self.assertEqual(fn('////server/path/to/file'), '\\\\server\\path\\to\\file')
+ self.assertEqual(fn('/////server/path/to/file'), '\\\\server\\path\\to\\file')
+ # Localhost paths
+ self.assertEqual(fn('//localhost/C:/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn('//localhost/C|/path/to/file'), 'C:\\path\\to\\file')
+ self.assertEqual(fn('//localhost/path/to/file'), '\\path\\to\\file')
+ self.assertEqual(fn('//localhost//server/path/to/file'), '\\\\server\\path\\to\\file')
+ # Percent-encoded forward slashes are preserved for backwards compatibility
+ self.assertEqual(fn('C:/foo%2fbar'), 'C:\\foo/bar')
+ self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar')
+ # Round-tripping
+ paths = ['C:',
+ r'\C\test\\',
+ r'C:\foo\bar\spam.foo']
+ for path in paths:
+ self.assertEqual(fn(nturl2path.pathname2url(path)), path)
+
+
+if __name__ == '__main__':
+ unittest.main()
# Code move from the old urllib module
-# Helper for non-unix systems
-if os.name == 'nt':
- from nturl2path import url2pathname, pathname2url
-else:
- def url2pathname(pathname):
- """OS-specific conversion from a relative URL of the 'file' scheme
- to a file system path; not recommended for general use."""
- if pathname[:3] == '///':
- # URL has an empty authority section, so the path begins on the
- # third character.
- pathname = pathname[2:]
- elif pathname[:12] == '//localhost/':
- # Skip past 'localhost' authority.
- pathname = pathname[11:]
- encoding = sys.getfilesystemencoding()
- errors = sys.getfilesystemencodeerrors()
- return unquote(pathname, encoding=encoding, errors=errors)
-
- def pathname2url(pathname):
- """OS-specific conversion from a file system path to a relative URL
- of the 'file' scheme; not recommended for general use."""
- if pathname[:1] == '/':
- # Add explicitly empty authority to absolute path. If the path
- # starts with exactly one slash then this change is mostly
- # cosmetic, but if it begins with two or more slashes then this
- # avoids interpreting the path as a URL authority.
- pathname = '//' + pathname
- encoding = sys.getfilesystemencoding()
- errors = sys.getfilesystemencodeerrors()
- return quote(pathname, encoding=encoding, errors=errors)
+def url2pathname(url):
+ """OS-specific conversion from a relative URL of the 'file' scheme
+ to a file system path; not recommended for general use."""
+ if url[:3] == '///':
+ # Empty authority section, so the path begins on the third character.
+ url = url[2:]
+ elif url[:12] == '//localhost/':
+ # Skip past 'localhost' authority.
+ url = url[11:]
+
+ if os.name == 'nt':
+ if url[:3] == '///':
+ # Skip past extra slash before UNC drive in URL path.
+ url = url[1:]
+ else:
+ if url[:1] == '/' and url[2:3] in (':', '|'):
+ # Skip past extra slash before DOS drive in URL path.
+ url = url[1:]
+ if url[1:2] == '|':
+ # Older URLs use a pipe after a drive letter
+ url = url[:1] + ':' + url[2:]
+ url = url.replace('/', '\\')
+ encoding = sys.getfilesystemencoding()
+ errors = sys.getfilesystemencodeerrors()
+ return unquote(url, encoding=encoding, errors=errors)
+
+
+def pathname2url(pathname):
+ """OS-specific conversion from a file system path to a relative URL
+ of the 'file' scheme; not recommended for general use."""
+ if os.name == 'nt':
+ pathname = pathname.replace('\\', '/')
+ encoding = sys.getfilesystemencoding()
+ errors = sys.getfilesystemencodeerrors()
+ drive, root, tail = os.path.splitroot(pathname)
+ if drive:
+ # First, clean up some special forms. We are going to sacrifice the
+ # additional information anyway
+ if drive[:4] == '//?/':
+ drive = drive[4:]
+ if drive[:4].upper() == 'UNC/':
+ drive = '//' + drive[4:]
+ if drive[1:] == ':':
+ # DOS drive specified. Add three slashes to the start, producing
+ # an authority section with a zero-length authority, and a path
+ # section starting with a single slash.
+ drive = '///' + drive
+ drive = quote(drive, encoding=encoding, errors=errors, safe='/:')
+ elif root:
+ # Add explicitly empty authority to absolute path. If the path
+ # starts with exactly one slash then this change is mostly
+ # cosmetic, but if it begins with two or more slashes then this
+ # avoids interpreting the path as a URL authority.
+ root = '//' + root
+ tail = quote(tail, encoding=encoding, errors=errors)
+ return drive + root + tail
# Utility functions