Skip to content

Commit 6ba134f

Browse files
volayaduckontheweb
authored andcommitted
Allow saving Catalog at location other than self href
1 parent 85e4602 commit 6ba134f

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## [Unreleased]
44

55
### Added
6+
- Optional `dest_href` argument to `Catalog.save` to allow saving `Catalog` instances to
7+
locations other than their `self` href ([#565](https://github.com/stac-utils/pystac/pull/565))
68

79
### Removed
810

pystac/catalog.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
identify_stac_object,
3030
migrate_to_latest,
3131
)
32-
from pystac.utils import is_absolute_href, make_absolute_href
32+
from pystac.utils import is_absolute_href, make_absolute_href, make_relative_href
3333

3434
if TYPE_CHECKING:
3535
from pystac.item import Asset as Asset_Type, Item as Item_Type
@@ -705,16 +705,22 @@ def generate_subcatalogs(
705705

706706
return result
707707

708-
def save(self, catalog_type: Optional[CatalogType] = None) -> None:
708+
def save(
709+
self,
710+
catalog_type: Optional[CatalogType] = None,
711+
dest_href: Optional[str] = None,
712+
) -> None:
709713
"""Save this catalog and all it's children/item to files determined by the object's
710-
self link HREF.
714+
self link HREF or a specified path.
711715
712716
Args:
713717
catalog_type : The catalog type that dictates the structure of
714718
the catalog to save. Use a member of :class:`~pystac.CatalogType`.
715719
If not supplied, the catalog_type of this catalog will be used.
716720
If that attribute is not set, an exception will be raised.
717-
721+
dest_href : The location where the catalog is to be saved.
722+
If not supplied, the catalog's self link HREF is used to determine
723+
the location of the catalog file and children's files.
718724
Note:
719725
If the catalog type is ``CatalogType.ABSOLUTE_PUBLISHED``,
720726
all self links will be included, and hierarchical links be absolute URLs.
@@ -724,6 +730,7 @@ def save(self, catalog_type: Optional[CatalogType] = None) -> None:
724730
If the catalog type is ``CatalogType.SELF_CONTAINED``, no self links will
725731
be included and hierarchical links will be relative URLs.
726732
"""
733+
727734
root = self.get_root()
728735
if root is None:
729736
raise Exception("There is no root catalog")
@@ -735,13 +742,27 @@ def save(self, catalog_type: Optional[CatalogType] = None) -> None:
735742

736743
for child_link in self.get_child_links():
737744
if child_link.is_resolved():
738-
cast(Catalog, child_link.target).save()
745+
child = cast(Catalog, child_link.target)
746+
if dest_href is not None:
747+
rel_href = make_relative_href(child.self_href, self.self_href)
748+
child_dest_href = make_absolute_href(
749+
rel_href, dest_href, start_is_dir=True
750+
)
751+
child.save(dest_href=child_dest_href)
752+
else:
753+
child.save()
739754

740755
for item_link in self.get_item_links():
741756
if item_link.is_resolved():
742-
cast(pystac.Item, item_link.target).save_object(
743-
include_self_link=items_include_self_link
744-
)
757+
item = cast(pystac.Item, item_link.target)
758+
if dest_href is not None:
759+
rel_href = make_relative_href(item.self_href, self.self_href)
760+
item_dest_href = make_absolute_href(
761+
rel_href, dest_href, start_is_dir=True
762+
)
763+
item.save_object(include_self_link=True, dest_href=item_dest_href)
764+
else:
765+
item.save_object(include_self_link=items_include_self_link)
745766

746767
include_self_link = False
747768
# include a self link if this is the root catalog
@@ -753,7 +774,15 @@ def save(self, catalog_type: Optional[CatalogType] = None) -> None:
753774
if root_link and root_link.get_absolute_href() == self.get_self_href():
754775
include_self_link = True
755776

756-
self.save_object(include_self_link=include_self_link)
777+
catalog_dest_href = None
778+
if dest_href is not None:
779+
rel_href = make_relative_href(self.self_href, self.self_href)
780+
catalog_dest_href = make_absolute_href(
781+
rel_href, dest_href, start_is_dir=True
782+
)
783+
self.save_object(
784+
include_self_link=include_self_link, dest_href=catalog_dest_href
785+
)
757786
if catalog_type is not None:
758787
self.catalog_type = catalog_type
759788

tests/test_catalog.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,20 @@ def test_save_uses_previous_catalog_type(self) -> None:
318318
cat2 = pystac.Catalog.from_file(href)
319319
self.assertEqual(cat2.catalog_type, CatalogType.SELF_CONTAINED)
320320

321+
def test_save_to_provided_href(self) -> None:
322+
with tempfile.TemporaryDirectory() as tmp_dir:
323+
catalog = TestCases.test_case_1()
324+
href = "http://test.com"
325+
folder = os.path.join(tmp_dir, "cat")
326+
catalog.normalize_hrefs(href)
327+
catalog.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED, dest_href=folder)
328+
329+
catalog_path = os.path.join(folder, "catalog.json")
330+
self.assertTrue(os.path.exists(catalog_path))
331+
result_cat = Catalog.from_file(catalog_path)
332+
for link in result_cat.get_child_links():
333+
self.assertTrue(cast(str, link.target).startswith(href))
334+
321335
def test_clone_uses_previous_catalog_type(self) -> None:
322336
catalog = TestCases.test_case_1()
323337
assert catalog.catalog_type == CatalogType.SELF_CONTAINED

0 commit comments

Comments
 (0)