Skip to content

Resolving a self link can erase href information #499

@gadomski

Description

@gadomski

As I am implementing stac-utils/stactools#151, I discovered what looks like a bug in link resolution. If the self link is resolved on a Collection STACObject, the target of the link is set to the Collection that object. This erases the string href information, which causes a break when later resolving an Item inside that Collection trying to use that link for other resolutions. I'm not opening a PR yet because I'm not quite sure how to fix it.

I realize that resolving a self link with an already-resolved root is a little weird and might feel artificial, but tThe cast at

self_link = self.get_single_link(pystac.RelType.SELF)
if self_link:
return cast(str, self_link.target)
of an attribute that might not be a str feels like something that could be a Real Bug™ later, so I figure this test case is an alright way of hammering that out.

Example

EDIT: Updated with a simpler example.

An example test case is here: https://github.com/gadomski/pystac/blob/4f6b2ee61e365612320e17375fe52eecebb93b50/tests/test_link.py#L88-L98. The test code looks like this:

def test_resolving_self(self) -> None:
    catalog = pystac.Catalog(id="test", description="test desc")
    with TemporaryDirectory() as temporary_directory:
        catalog.normalize_and_save(temporary_directory)
        collection = pystac.Catalog.from_file(
            os.path.join(temporary_directory, "catalog.json")
        )
        link = collection.get_single_link(pystac.RelType.SELF)
        assert link
        link.resolve_stac_object()
        link.get_absolute_href()

The error:

pystac/link.py:161: in get_absolute_href
    href = make_absolute_href(href, self.owner.get_self_href())
pystac/utils.py:237: in make_absolute_href
    parsed_start = safe_urlparse(start_href)
pystac/utils.py:21: in safe_urlparse
    parsed = urlparse(href)
/usr/local/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/parse.py:392: in urlparse
    url, scheme, _coerce_result = _coerce_args(url, scheme)
/usr/local/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/parse.py:128: in _coerce_args
    return _decode_args(args) + (_encode_result,)
/usr/local/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/parse.py:112: in _decode_args
    return tuple(x.decode(encoding, errors) if x else '' for x in args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

.0 = <tuple_iterator object at 0x10f48ec40>

>   return tuple(x.decode(encoding, errors) if x else '' for x in args)
E   AttributeError: 'Catalog' object has no attribute 'decode'

/usr/local/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/parse.py:112: AttributeError

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions