Skip to content

Commit

Permalink
Added support to filepath_from_url for UNC paths and tests for UNC an…
Browse files Browse the repository at this point in the history
…d posix paths (#1674)

* Added support for UNC paths and tests for UNC and posix paths

---------

Signed-off-by: Doug Halley <[email protected]>
  • Loading branch information
douglascomet authored Mar 28, 2024
1 parent ddb39f5 commit 82068e3
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
34 changes: 28 additions & 6 deletions src/py-opentimelineio/opentimelineio/url_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ def filepath_from_url(urlstr):
Take an url and return a filepath.
URLs can either be encoded according to the `RFC 3986`_ standard or not.
Additionally, Windows mapped paths need to be accounted for when processing a
URL; however, there are `ongoing discussions`_ about how to best handle this within
Python. This function is meant to cover all of these scenarios in the interim.
Additionally, Windows mapped drive letter and UNC paths need to be accounted for
when processing URL(s); however, there are `ongoing discussions`_ about how to best
handle this within Python developer community. This function is meant to cover
these scenarios in the interim.
.. _RFC 3986: https://tools.ietf.org/html/rfc3986#section-2.1
.. _ongoing discussions: https://discuss.python.org/t/file-uris-in-python/15600
Expand All @@ -56,17 +57,38 @@ def filepath_from_url(urlstr):
# Parse provided URL
parsed_result = urlparse.urlparse(urlstr)

# De-encode the parsed path
decoded_parsed_path = urlparse.unquote(parsed_result.path)

# Convert the parsed URL to a path
filepath = PurePath(request.url2pathname(parsed_result.path))
filepath = PurePath(request.url2pathname(decoded_parsed_path))

# If the network location is a window drive, reassemble the path
if PureWindowsPath(parsed_result.netloc).drive:
filepath = PurePath(parsed_result.netloc + parsed_result.path)
filepath = PurePath(parsed_result.netloc + decoded_parsed_path)

# If the specified index is a windows drive, then append it to the other parts
elif PureWindowsPath(filepath.parts[0]).drive:
filepath = PurePosixPath(filepath.drive, *filepath.parts[1:])

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

# Should catch UNC paths,
# as parsing "file:///some/path/to/file.ext" doesn't provide a netloc
elif parsed_result.netloc and parsed_result.netloc != 'localhost':
# Paths of type: "file://host/share/path/to/file.ext" provide "host" as netloc
filepath = PurePath('//', parsed_result.netloc + decoded_parsed_path)

# Executing `as_posix` on Windows seems to generate a path with only
# 1 leading `/`, so we insert another `/` at the front of the string path
# to match Linux and Windows UNC conventions and return it.
conformed_filepath = filepath.as_posix()
if not conformed_filepath.startswith('//'):
conformed_filepath = '/' + conformed_filepath
return conformed_filepath

# Convert "\" to "/" if needed
return filepath.as_posix()
29 changes: 24 additions & 5 deletions tests/test_url_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,18 @@
MEDIA_EXAMPLE_PATH_ABS
)

ENCODED_WINDOWS_URL = "file://localhost/S%3a/path/file.ext"
WINDOWS_URL = "file://S:/path/file.ext"
CORRECTED_WINDOWS_PATH = "S:/path/file.ext"
WINDOWS_ENCODED_URL = "file://host/S%3a/path/file.ext"
WINDOWS_DRIVE_URL = "file://S:/path/file.ext"
WINDOWS_DRIVE_PATH = "S:/path/file.ext"

WINDOWS_ENCODED_UNC_URL = "file://unc/path/sub%20dir/file.ext"
WINDOWS_UNC_URL = "file://unc/path/sub dir/file.ext"
WINDOWS_UNC_PATH = "//unc/path/sub dir/file.ext"

POSIX_LOCALHOST_URL = "file://localhost/path/sub dir/file.ext"
POSIX_ENCODED_URL = "file:///path/sub%20dir/file.ext"
POSIX_URL = "file:///path/sub dir/file.ext"
POSIX_PATH = "/path/sub dir/file.ext"


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

def test_windows_urls(self):
for url in (ENCODED_WINDOWS_URL, WINDOWS_URL):
for url in (WINDOWS_ENCODED_URL, WINDOWS_DRIVE_URL):
processed_url = otio.url_utils.filepath_from_url(url)
self.assertEqual(processed_url, WINDOWS_DRIVE_PATH)

def test_windows_unc_urls(self):
for url in (WINDOWS_ENCODED_UNC_URL, WINDOWS_UNC_URL):
processed_url = otio.url_utils.filepath_from_url(url)
self.assertEqual(processed_url, WINDOWS_UNC_PATH)

def test_posix_urls(self):
for url in (POSIX_ENCODED_URL, POSIX_URL, POSIX_LOCALHOST_URL):
processed_url = otio.url_utils.filepath_from_url(url)
self.assertEqual(processed_url, CORRECTED_WINDOWS_PATH)
self.assertEqual(processed_url, POSIX_PATH)


if __name__ == "__main__":
Expand Down

0 comments on commit 82068e3

Please sign in to comment.