Skip to content

Commit

Permalink
Adds support to filepath_from_url to support non-encoded URLs with …
Browse files Browse the repository at this point in the history
…Windows drive letters

* Add support to `filepath_from_url` to support non-encoded URLs with Windows drive letters
* Added additional unit tests

Signed-off-by: Doug Halley <[email protected]>
  • Loading branch information
douglascomet authored Oct 13, 2023
1 parent c369126 commit 745e614
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 4 deletions.
36 changes: 32 additions & 4 deletions src/py-opentimelineio/opentimelineio/url_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
parse as urlparse,
request
)
import pathlib
from pathlib import (
Path,
PureWindowsPath
)


def url_from_filepath(fpath):
Expand All @@ -18,7 +21,7 @@ def url_from_filepath(fpath):
try:
# appears to handle absolute windows paths better, which are absolute
# and start with a drive letter.
return urlparse.unquote(pathlib.Path(fpath).as_uri())
return urlparse.unquote(Path(fpath).as_uri())
except ValueError:
# scheme is "file" for absolute paths, else ""
scheme = "file" if os.path.isabs(fpath) else ""
Expand All @@ -37,7 +40,32 @@ def url_from_filepath(fpath):


def filepath_from_url(urlstr):
""" Take a url and return a filepath """
"""
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.
.. _RFC 3986: https://tools.ietf.org/html/rfc3986#section-2.1
.. _ongoing discussions: https://discuss.python.org/t/file-uris-in-python/15600
"""

# Parse provided URL
parsed_result = urlparse.urlparse(urlstr)
return request.url2pathname(parsed_result.path)

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

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

# Otherwise check 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 = filepath.relative_to(filepath.root)

# Convert "\" to "/" if needed
return filepath.as_posix()
9 changes: 9 additions & 0 deletions tests/test_url_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
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"


class TestConversions(unittest.TestCase):
def test_roundtrip_abs(self):
Expand All @@ -51,6 +55,11 @@ def test_roundtrip_rel(self):
# should have reconstructed it by this point
self.assertEqual(os.path.normpath(result), MEDIA_EXAMPLE_PATH_REL)

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


if __name__ == "__main__":
unittest.main()

0 comments on commit 745e614

Please sign in to comment.