Skip to content

Commit 82068e3

Browse files
authored
Added support to filepath_from_url for UNC paths and tests for UNC and posix paths (AcademySoftwareFoundation#1674)
* Added support for UNC paths and tests for UNC and posix paths --------- Signed-off-by: Doug Halley <[email protected]>
1 parent ddb39f5 commit 82068e3

File tree

2 files changed

+52
-11
lines changed

2 files changed

+52
-11
lines changed

src/py-opentimelineio/opentimelineio/url_utils.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ def filepath_from_url(urlstr):
4545
Take an url and return a filepath.
4646
4747
URLs can either be encoded according to the `RFC 3986`_ standard or not.
48-
Additionally, Windows mapped paths need to be accounted for when processing a
49-
URL; however, there are `ongoing discussions`_ about how to best handle this within
50-
Python. This function is meant to cover all of these scenarios in the interim.
48+
Additionally, Windows mapped drive letter and UNC paths need to be accounted for
49+
when processing URL(s); however, there are `ongoing discussions`_ about how to best
50+
handle this within Python developer community. This function is meant to cover
51+
these scenarios in the interim.
5152
5253
.. _RFC 3986: https://tools.ietf.org/html/rfc3986#section-2.1
5354
.. _ongoing discussions: https://discuss.python.org/t/file-uris-in-python/15600
@@ -56,17 +57,38 @@ def filepath_from_url(urlstr):
5657
# Parse provided URL
5758
parsed_result = urlparse.urlparse(urlstr)
5859

60+
# De-encode the parsed path
61+
decoded_parsed_path = urlparse.unquote(parsed_result.path)
62+
5963
# Convert the parsed URL to a path
60-
filepath = PurePath(request.url2pathname(parsed_result.path))
64+
filepath = PurePath(request.url2pathname(decoded_parsed_path))
6165

6266
# If the network location is a window drive, reassemble the path
6367
if PureWindowsPath(parsed_result.netloc).drive:
64-
filepath = PurePath(parsed_result.netloc + parsed_result.path)
68+
filepath = PurePath(parsed_result.netloc + decoded_parsed_path)
69+
70+
# If the specified index is a windows drive, then append it to the other parts
71+
elif PureWindowsPath(filepath.parts[0]).drive:
72+
filepath = PurePosixPath(filepath.drive, *filepath.parts[1:])
6573

66-
# Otherwise check if the specified index is a windows drive, then offset the path
74+
# If the specified index is a windows drive, then offset the path
6775
elif PureWindowsPath(filepath.parts[1]).drive:
6876
# Remove leading "/" if/when `request.url2pathname` yields "/S:/path/file.ext"
6977
filepath = PurePosixPath(*filepath.parts[1:])
7078

79+
# Should catch UNC paths,
80+
# as parsing "file:///some/path/to/file.ext" doesn't provide a netloc
81+
elif parsed_result.netloc and parsed_result.netloc != 'localhost':
82+
# Paths of type: "file://host/share/path/to/file.ext" provide "host" as netloc
83+
filepath = PurePath('//', parsed_result.netloc + decoded_parsed_path)
84+
85+
# Executing `as_posix` on Windows seems to generate a path with only
86+
# 1 leading `/`, so we insert another `/` at the front of the string path
87+
# to match Linux and Windows UNC conventions and return it.
88+
conformed_filepath = filepath.as_posix()
89+
if not conformed_filepath.startswith('//'):
90+
conformed_filepath = '/' + conformed_filepath
91+
return conformed_filepath
92+
7193
# Convert "\" to "/" if needed
7294
return filepath.as_posix()

tests/test_url_conversions.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,18 @@
3232
MEDIA_EXAMPLE_PATH_ABS
3333
)
3434

35-
ENCODED_WINDOWS_URL = "file://localhost/S%3a/path/file.ext"
36-
WINDOWS_URL = "file://S:/path/file.ext"
37-
CORRECTED_WINDOWS_PATH = "S:/path/file.ext"
35+
WINDOWS_ENCODED_URL = "file://host/S%3a/path/file.ext"
36+
WINDOWS_DRIVE_URL = "file://S:/path/file.ext"
37+
WINDOWS_DRIVE_PATH = "S:/path/file.ext"
38+
39+
WINDOWS_ENCODED_UNC_URL = "file://unc/path/sub%20dir/file.ext"
40+
WINDOWS_UNC_URL = "file://unc/path/sub dir/file.ext"
41+
WINDOWS_UNC_PATH = "//unc/path/sub dir/file.ext"
42+
43+
POSIX_LOCALHOST_URL = "file://localhost/path/sub dir/file.ext"
44+
POSIX_ENCODED_URL = "file:///path/sub%20dir/file.ext"
45+
POSIX_URL = "file:///path/sub dir/file.ext"
46+
POSIX_PATH = "/path/sub dir/file.ext"
3847

3948

4049
class TestConversions(unittest.TestCase):
@@ -56,9 +65,19 @@ def test_roundtrip_rel(self):
5665
self.assertEqual(os.path.normpath(result), MEDIA_EXAMPLE_PATH_REL)
5766

5867
def test_windows_urls(self):
59-
for url in (ENCODED_WINDOWS_URL, WINDOWS_URL):
68+
for url in (WINDOWS_ENCODED_URL, WINDOWS_DRIVE_URL):
69+
processed_url = otio.url_utils.filepath_from_url(url)
70+
self.assertEqual(processed_url, WINDOWS_DRIVE_PATH)
71+
72+
def test_windows_unc_urls(self):
73+
for url in (WINDOWS_ENCODED_UNC_URL, WINDOWS_UNC_URL):
74+
processed_url = otio.url_utils.filepath_from_url(url)
75+
self.assertEqual(processed_url, WINDOWS_UNC_PATH)
76+
77+
def test_posix_urls(self):
78+
for url in (POSIX_ENCODED_URL, POSIX_URL, POSIX_LOCALHOST_URL):
6079
processed_url = otio.url_utils.filepath_from_url(url)
61-
self.assertEqual(processed_url, CORRECTED_WINDOWS_PATH)
80+
self.assertEqual(processed_url, POSIX_PATH)
6281

6382

6483
if __name__ == "__main__":

0 commit comments

Comments
 (0)