Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/obspec_utils/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,17 @@ def resolve(self, url: Url) -> tuple[T, Path]:
```
"""
parsed = urlparse(url)
# Object-store keys can contain ";", "?" and "#", which urlparse peels off
# into the URL params/query/fragment. These characters are valid in
# S3/GCS/Azure keys (e.g. "DC_DCAM#1_CAM3.tif"), so reattach them to
# reconstruct the full path; otherwise the key would be silently truncated.
path = parsed.path
if parsed.params:
path = f"{path};{parsed.params}"
if parsed.query:
path = f"{path}?{parsed.query}"
if parsed.fragment:
path = f"{path}#{parsed.fragment}"

key = UrlKey(parsed.scheme, parsed.netloc)

Expand Down
20 changes: 20 additions & 0 deletions tests/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ def test_registry():
assert ret is memstore


@pytest.mark.parametrize(
("url", "expected"),
[
# "#" is valid in object-store keys but urlparse treats it as a fragment.
("s3://bucket1/path/to/DC_DCAM#1_CAM3.tif", "path/to/DC_DCAM#1_CAM3.tif"),
# "?" is valid too and urlparse treats it as a query string.
("s3://bucket1/path/to/obj?v=1.tif", "path/to/obj?v=1.tif"),
# ";" in the final segment is treated as params by urlparse.
("s3://bucket1/path/to/obj;a=b.tif", "path/to/obj;a=b.tif"),
],
)
def test_resolve_preserves_special_chars_in_key(url, expected):
registry = ObjectStoreRegistry()
memstore = MemoryStore()
registry.register("s3://bucket1", memstore)
ret, path = registry.resolve(url)
assert path == expected
assert ret is memstore


def test_register_raises():
registry = ObjectStoreRegistry()
with pytest.raises(
Expand Down