diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d43cfcc8..9135cf462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Enable [strict mode](https://mypy.readthedocs.io/en/latest/command_line.html?highlight=strict%20mode#cmdoption-mypy-strict) for `mypy` ([#591](https://github.com/stac-utils/pystac/pull/591)) +- Links will get their `title` from their target if no `title` is provided ([#607](https://github.com/stac-utils/pystac/pull/607)) ### Fixed diff --git a/pystac/link.py b/pystac/link.py index 11a22f175..7de266bbf 100644 --- a/pystac/link.py +++ b/pystac/link.py @@ -56,9 +56,6 @@ class Link: """Optional description of the media type. Registered Media Types are preferred. See :class:`~pystac.MediaType` for common media types.""" - title: Optional[str] - """Optional title for this link.""" - extra_fields: Dict[str, Any] """Optional, additional fields for this link. This is used by extensions as a way to serialize and deserialize properties on link object JSON.""" @@ -70,6 +67,7 @@ class Link: _target_href: Optional[str] _target_object: Optional["STACObject_Type"] + _title: Optional[str] def __init__( self, @@ -103,6 +101,22 @@ def set_owner(self, owner: Optional["STACObject_Type"]) -> "Link": self.owner = owner return self + @property + def title(self) -> Optional[str]: + """Optional title for this link. If not provided during instantiation, this will + attempt to get the title from the STAC object that the link references.""" + if self._title is not None: + return self._title + if self._target_object is not None and isinstance( + self._target_object, pystac.Catalog + ): + return self._target_object.title + return None + + @title.setter + def title(self, v: Optional[str]) -> None: + self._title = v + @property def href(self) -> str: """Returns the HREF for this link. diff --git a/tests/test_link.py b/tests/test_link.py index 8a69d6ff8..48ada4c54 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -130,6 +130,62 @@ def test_relative_self_href(self) -> None: finally: os.chdir(previous) + def test_auto_title_when_resolved(self) -> None: + extent = pystac.Extent.from_items([self.item]) + collection = pystac.Collection( + id="my_collection", + description="Test Collection", + extent=extent, + title="Collection Title", + ) + link = pystac.Link("my rel", target=collection) + + self.assertEqual(collection.title, link.title) + + def test_auto_title_not_found(self) -> None: + extent = pystac.Extent.from_items([self.item]) + collection = pystac.Collection( + id="my_collection", + description="Test Collection", + extent=extent, + ) + link = pystac.Link("my rel", target=collection) + + self.assertEqual(None, link.title) + + def test_auto_title_is_serialized(self) -> None: + extent = pystac.Extent.from_items([self.item]) + collection = pystac.Collection( + id="my_collection", + description="Test Collection", + extent=extent, + title="Collection Title", + ) + link = pystac.Link("my rel", target=collection) + + assert link.to_dict().get("title") == collection.title + + def test_no_auto_title_if_not_resolved(self) -> None: + link = pystac.Link( + "my rel", target="https://www.some-domain.com/path/to/thing.txt" + ) + + assert link.title is None + + def test_title_as_init_argument(self) -> None: + link_title = "Link title" + extent = pystac.Extent.from_items([self.item]) + collection = pystac.Collection( + id="my_collection", + description="Test Collection", + extent=extent, + title="Collection Title", + ) + link = pystac.Link("my rel", title=link_title, target=collection) + + assert link.title == link_title + assert link.to_dict().get("title") == link_title + class StaticLinkTest(unittest.TestCase): def setUp(self) -> None: