Skip to content

Commit

Permalink
Merge pull request #290 from matthewhanson/remove_link_type
Browse files Browse the repository at this point in the history
Remove link type
  • Loading branch information
matthewhanson authored Mar 25, 2021
2 parents e0768a8 + e18d4de commit 3645114
Show file tree
Hide file tree
Showing 18 changed files with 115 additions and 342 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

### Added

- HIERARCHICAL_LINKS array constant of all the types of hierarchical links (self is not included)

### Fixed

- Fixed error when accessing the statistics attribute of the pointcloud extension when no statistics were defined ([#282](https://github.com/stac-utils/pystac/pull/282))
- Fixed exception being thrown when calling set_self_href on items with assets that have relative hrefs ([#291](https://github.com/stac-utils/pystac/pull/291))

### Changed

- Link behavior - link URLs can be either relative or absolute. Hierarchical (e.g., parent, child) links are made relative or absolute based on the value of the root catalog's `catalog_type` field

### Removed

- Removed LinkType class and the `link_type` field from links

## [v0.5.5]

### Added
Expand Down
15 changes: 9 additions & 6 deletions docs/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,17 @@ Relative vs Absolute Link HREFs

Absolute links point to their file locations in a fully described way. Relative links
are relative to the linking object's file location. For example, if a catalog at
``/some/location/catalog.json`` has a link to an item that has an HREF set to ``item-id/item-id.json``, then that link should resolve to the absolute path ``/some/location/item-id/item-id.json``.
``/some/location/catalog.json`` has a link to an item that has an HREF set to ``item-id/item-id.json``,
then that link should resolve to the absolute path ``/some/location/item-id/item-id.json``.

The implementation of :class:`~pystac.Link` in PySTAC allows for the link to be marked as
``link_type=LinkType.ABSOLUTE`` or ``link_type=LinkType.RELATIVE``. This means that,
even if the stored HREF of the link is absolute, if the link is marked as relative, serializing
the link will produce a relative link, based on the self link of the parent object.
Links are set as absolute or relative HREFs at save time, as determine by the root catalog's catalog_type
:attribute:`~pystac.Catalog.catalog_type`. This means that, even if the stored HREF of the link is absolute,
if the root ``catalog_type=CatalogType.RELATIVE_PUBLISHED`` or ``catalog_type=CatalogType.SELF_CONTAINED``
and subsequent serializing of the any links in the catalog will produce a relative link,
based on the self link of the parent object.

You can make all the links of a catalog relative or absolute using the :func:`Catalog.make_all_links_relative <pystac.Catalog.make_all_links_relative>` and :func:`Catalog.make_all_links_absolute <pystac.Catalog.make_all_links_absolute>` methods.
You can make all the links of a catalog relative or absolute by setting the :func:`Catalog.catalog_type` field
then resaving the entire catalog.

.. _rel vs abs asset:

Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/adding-new-and-custom-extensions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
"source": [
"The `from_item` class method simply returns a new instance of the item extension given an item.\n",
"\n",
"The `_object_links` class method returns the `rel` string for any links that point to STAC objects like Catalogs, Collections or Items. PySTAC needs to know which links point to STAC objects because it needs to consider them when fully resolving a STAC into in-memory objects. In a lot of cases, extensions don't add new links to STAC objects, so this is normally an empty list; however, if the extension does do this (like the `source` link in the [Label Extension](https://github.com/radiantearth/stac-spec/tree/v1.0.0-beta.2/extensions/label#links-source-imagery)), make sure to return the correct value (like the LabelItemExt is doing [here](https://github.com/azavea/pystac/blob/v0.5.0/pystac/extensions/label.py#L291-L293))."
"The `_object_links` class method returns the `rel` string for any links that point to STAC objects like Catalogs, Collections or Items. PySTAC needs to know which links point to STAC objects because it needs to consider them when fully resolving a STAC into in-memory objects. It also will use this information when deciding on whether to use absolute or relative HREFs for the links, based on the root catalog type. In a lot of cases, extensions don't add new links to STAC objects, so this is normally an empty list; however, if the extension does do this (like the `source` link in the [Label Extension](https://github.com/radiantearth/stac-spec/tree/v1.0.0-beta.2/extensions/label#links-source-imagery)), make sure to return the correct value (like the LabelItemExt is doing [here](https://github.com/azavea/pystac/blob/v0.5.0/pystac/extensions/label.py#L291-L293))."
]
},
{
Expand Down Expand Up @@ -539,4 +539,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
2 changes: 1 addition & 1 deletion pystac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class STACError(Exception):
from pystac.extensions import Extensions
from pystac.stac_object import (STACObject, STACObjectType)
from pystac.media_type import MediaType
from pystac.link import (Link, LinkType)
from pystac.link import (Link, HIERARCHICAL_LINKS)
from pystac.catalog import (Catalog, CatalogType)
from pystac.collection import (Collection, Extent, SpatialExtent, TemporalExtent, Provider)
from pystac.item import (Item, Asset, CommonMetadata)
Expand Down
90 changes: 29 additions & 61 deletions pystac/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pystac import STACError
from pystac.stac_object import STACObject
from pystac.layout import (BestPracticesLayoutStrategy, LayoutTemplate)
from pystac.link import (Link, LinkType)
from pystac.link import Link
from pystac.cache import ResolvedObjectCache
from pystac.utils import (is_absolute_href, make_absolute_href)

Expand Down Expand Up @@ -117,7 +117,7 @@ def __init__(self,
stac_extensions=None,
extra_fields=None,
href=None,
catalog_type=None):
catalog_type=CatalogType.ABSOLUTE_PUBLISHED):
super().__init__(stac_extensions)

self.id = id
Expand All @@ -142,12 +142,15 @@ def __init__(self,
def __repr__(self):
return '<Catalog id={}>'.format(self.id)

def set_root(self, root, link_type=LinkType.ABSOLUTE):
STACObject.set_root(self, root, link_type)
def set_root(self, root):
STACObject.set_root(self, root)
if root is not None:
root._resolved_objects = ResolvedObjectCache.merge(root._resolved_objects,
self._resolved_objects)

def is_relative(self):
return self.catalog_type in [CatalogType.RELATIVE_PUBLISHED, CatalogType.SELF_CONTAINED]

def add_child(self, child, title=None):
"""Adds a link to a child :class:`~pystac.Catalog` or :class:`~pystac.Collection`.
This method will set the child's parent to this object, and its root to
Expand Down Expand Up @@ -405,28 +408,6 @@ def clone(self):

return clone

def make_all_links_relative(self):
"""Makes all the links of this catalog and all children and item
to be relative, recursively
"""
super().make_links_relative()

for child in self.get_children():
child.make_all_links_relative()
for item in self.get_items():
item.make_links_relative()

def make_all_links_absolute(self):
"""Makes all the links of this catalog and all children and item
to be absolute, recursively
"""
super().make_links_absolute()

for child in self.get_children():
child.make_all_links_absolute()
for item in self.get_items():
item.make_links_absolute()

def make_all_asset_hrefs_relative(self):
"""Makes all the HREFs of assets belonging to items in this catalog
and all children to be relative, recursively.
Expand All @@ -443,9 +424,8 @@ def make_all_asset_hrefs_absolute(self):
for item in items:
item.make_asset_hrefs_absolute()

def normalize_and_save(self, root_href, catalog_type, strategy=None):
"""Normalizes link HREFs to the given root_href, and saves
the catalog with the given catalog_type.
def normalize_and_save(self, root_href, catalog_type=None, strategy=None):
"""Normalizes link HREFs to the given root_href, and saves the catalog.
This is a convenience method that simply calls :func:`Catalog.normalize_hrefs
<pystac.Catalog.normalize_hrefs>` and :func:`Catalog.save <pystac.Catalog.save>`
Expand All @@ -455,6 +435,8 @@ def normalize_and_save(self, root_href, catalog_type, strategy=None):
root_href (str): The absolute HREF that all links will be normalized against.
catalog_type (str): The catalog type that dictates the structure of
the catalog to save. Use a member of :class:`~pystac.CatalogType`.
Defaults to the root catalog.catalog_type or the current catalog catalog_type
if there is no root catalog.
strategy (HrefLayoutStrategy): The layout strategy to use in setting the HREFS
for this catalog. Defaults to :class:`~pystac.layout.BestPracticesLayoutStrategy`
"""
Expand Down Expand Up @@ -593,51 +575,37 @@ def save(self, catalog_type=None):
Note:
If the catalog type is ``CatalogType.ABSOLUTE_PUBLISHED``,
all self links will be included, and link type will be set to ABSOLUTE.
all self links will be included, and hierarchical links be absolute URLs.
If the catalog type is ``CatalogType.RELATIVE_PUBLISHED``, this catalog's self
link will be included, but no child catalog will have self links.
Link types will be set to RELATIVE.
link will be included, but no child catalog will have self links, and
hierarchical links will be relative URLs
If the catalog type is ``CatalogType.SELF_CONTAINED``, no self links will be
included. Link types will be set to RELATIVE.
Raises:
ValueError: Raises if the catalog_type argument is not supplied and
there is no catalog_type attribute on this catalog.
included and hierarchical links will be relative URLs.
"""
catalog_type = catalog_type or self.catalog_type

if catalog_type is None:
raise ValueError('Must supply a catalog_type if one is not set on the catalog.')

# Ensure relative vs absolute
if catalog_type == CatalogType.ABSOLUTE_PUBLISHED:
self.make_all_links_absolute()
self.make_all_asset_hrefs_absolute()
elif catalog_type in (CatalogType.SELF_CONTAINED, CatalogType.RELATIVE_PUBLISHED):
self.make_all_links_relative()
self.make_all_asset_hrefs_relative()
else:
raise ValueError(f'catalog_type is not a CatalogType: "{catalog_type}"')

include_self_link = catalog_type in [
CatalogType.ABSOLUTE_PUBLISHED, CatalogType.RELATIVE_PUBLISHED
]
root = self.get_root()
if root is None:
raise Exception('There is no root catalog')

if catalog_type == CatalogType.RELATIVE_PUBLISHED:
child_catalog_type = CatalogType.SELF_CONTAINED
else:
child_catalog_type = catalog_type
if catalog_type is not None:
root.catalog_type = catalog_type

items_include_self_link = catalog_type in [CatalogType.ABSOLUTE_PUBLISHED]
items_include_self_link = root.catalog_type in [CatalogType.ABSOLUTE_PUBLISHED]

for child_link in self.get_child_links():
if child_link.is_resolved():
child_link.target.save(catalog_type=child_catalog_type)
child_link.target.save()

for item_link in self.get_item_links():
if item_link.is_resolved():
item_link.target.save_object(include_self_link=items_include_self_link)

include_self_link = False
# include a self link if this is the root catalog or if ABSOLUTE_PUBLISHED catalog
if ((self.get_self_href() == self.get_root_link().get_absolute_href()
and root.catalog_type != CatalogType.SELF_CONTAINED)
or root.catalog_type == CatalogType.ABSOLUTE_PUBLISHED):
include_self_link = True

self.save_object(include_self_link=include_self_link)

self.catalog_type = catalog_type
Expand Down
2 changes: 1 addition & 1 deletion pystac/extensions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class ExtendedObject:
"""ExtendedObject maps STACObject classes (Catalog, Collecition and Item) to
"""ExtendedObject maps STACObject classes (Catalog, Collection and Item) to
extension classes (classes that implement one of CatalogExtension, CollectionExtesion,
or ItemCollection). When an extension is registered with PySTAC it uses the registered
list of ExtendedObject to determine how to handle extending objects, e.g. when item.ext.label
Expand Down
14 changes: 3 additions & 11 deletions pystac/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pystac
from pystac import (STACError, STACObjectType)
from pystac.link import Link, LinkType
from pystac.link import Link
from pystac.stac_object import STACObject
from pystac.utils import (is_absolute_href, make_absolute_href, make_relative_href, datetime_to_str,
str_to_datetime)
Expand Down Expand Up @@ -221,7 +221,7 @@ def make_asset_hrefs_absolute(self):

return self

def set_collection(self, collection, link_type=None):
def set_collection(self, collection):
"""Set the collection of this item.
This method will replace any existing Collection link and attribute for
Expand All @@ -230,22 +230,14 @@ def set_collection(self, collection, link_type=None):
Args:
collection (Collection or None): The collection to set as this
item's collection. If None, will clear the collection.
link_type (str): the link type to use for the collection link.
One of :class:`~pystac.LinkType`.
Returns:
Item: self
"""
if not link_type:
prev = self.get_single_link('collection')
if prev is not None:
link_type = prev.link_type
else:
link_type = LinkType.ABSOLUTE
self.remove_links('collection')
self.collection_id = None
if collection is not None:
self.add_link(Link.collection(collection, link_type=link_type))
self.add_link(Link.collection(collection))
self.collection_id = collection.id

return self
Expand Down
Loading

0 comments on commit 3645114

Please sign in to comment.