From c24d0747192567d15da95156a21bdba53ad3e3cc Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Tue, 6 Jan 2026 08:40:21 -0500 Subject: [PATCH 1/4] Remove buggy skip in remote collection push --- .gitignore | 3 +++ henry/collection.py | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index ebfcfa9..d4e6e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ examples/*.fits *.tar.gz *.mp4 *product.json + +.nova/* +*.swp \ No newline at end of file diff --git a/henry/collection.py b/henry/collection.py index ac24db4..417e7d9 100644 --- a/henry/collection.py +++ b/henry/collection.py @@ -473,12 +473,14 @@ def _upload( readers: list[str] | None = None, writers: list[str] | None = None, ): - if self.collection_id: - # We've already been uploaded! - return self.colleciton_id + # Cannot do the usual skip as there's no 'revision' system for + # collections. So we must check first whether any of our children + # actually need to be uploaded. Because we know that this is a + # "RemoteCollection", too, there's no need to skip anything as + # we never actually upload 'self', only children. if not skip_preflight: - # This runs _all_ preflight checks - for all connected collecitons and products. + # This runs _all_ preflight checks - for all connected collections and products. self.preflight() for product in self.products: From 5707a74858778b0be115dc1d7e431078a9e4bb3e Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Tue, 6 Jan 2026 08:58:00 -0500 Subject: [PATCH 2/4] Rev version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fd300a9..db9d090 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ hipposerve = ["web/static/*.svg", "web/static/*.ico", "web/static/*.png", "web/t [project] name = "pyhippo" description = "The Hierarchical Product Post Office, developed for the Simons Observatory." -version = "0.2.8" +version = "0.2.9" readme = "README.md" requires-python = ">=3.11" dependencies = [ From bf0c4da7c9e71cb296f32e25cd677055d39a3f53 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Tue, 6 Jan 2026 10:43:07 -0500 Subject: [PATCH 3/4] Incorrect IDs were used in revision of Collection --- henry/collection.py | 14 +++++--- henry/core.py | 8 +++-- henry/product.py | 2 ++ hippoclient/core.py | 2 +- tests/test_client/conftest.py | 8 +++++ tests/test_client/test_collection.py | 53 ++++++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 tests/test_client/test_collection.py diff --git a/henry/collection.py b/henry/collection.py index 417e7d9..2c74360 100644 --- a/henry/collection.py +++ b/henry/collection.py @@ -19,6 +19,8 @@ class CollectionInstance(BaseModel): + collection_id: str | None + pass @@ -159,7 +161,7 @@ def _upload( return self.colleciton_id if not skip_preflight: - # This runs _all_ preflight checks - for all connected collecitons and products. + # This runs _all_ preflight checks - for all connected collections and products. self.preflight() self.collection_id = collections.create( @@ -295,7 +297,7 @@ def pull( ) @classmethod - def read(cls, directory: Path | str, allow_incomplete: bool) -> "RemoteProduct": + def read(cls, directory: Path | str, allow_incomplete: bool) -> "RemoteCollection": """ Read a collection that was serialized to disk, usually with the `henry collection download $ID` command. @@ -493,7 +495,9 @@ def _upload( ) product_ids_to_connect = [ - p.id for p in self.products if p.id not in self.original_product_ids + p.product_id + for p in self.products + if p.product_id not in self.original_product_ids ] for product_id in product_ids_to_connect: @@ -515,7 +519,9 @@ def _upload( # Recurse! child_collection_ids_to_connect = [ - c.id for c in self.collections if c.id not in self.original_collection_ids + c.collection_id + for c in self.collections + if c.collection_id not in self.original_collection_ids ] for collection_id in child_collection_ids_to_connect: diff --git a/henry/core.py b/henry/core.py index 3bc2d43..6662854 100644 --- a/henry/core.py +++ b/henry/core.py @@ -11,7 +11,7 @@ from hippometa import ALL_METADATA_TYPE from .collection import CollectionInstance, LocalCollection, RemoteCollection -from .product import LocalProduct, ProductInstance, RemoteProduct +from .product import LocalProduct, ProductInstance, RemoteProduct, RevisionProduct class Henry: @@ -66,7 +66,9 @@ def new_collection( ) def push( - self, item: LocalProduct | LocalCollection, skip_preflight: bool = False + self, + item: LocalProduct | LocalCollection | RevisionProduct | RemoteCollection, + skip_preflight: bool = False, ) -> str: return item._upload( client=self.client, @@ -99,7 +101,7 @@ def pull_collection( collection_id: str, realize_sources: bool = True, pull_children: bool = True, - ) -> CollectionInstance: + ) -> RemoteCollection: return RemoteCollection.pull( collection_id=collection_id, client=self.client, diff --git a/henry/product.py b/henry/product.py index a887735..f886388 100644 --- a/henry/product.py +++ b/henry/product.py @@ -15,6 +15,8 @@ class ProductInstance(BaseModel): + product_id: str | None + pass def _upload( diff --git a/hippoclient/core.py b/hippoclient/core.py index 31f9f03..8d8e9e2 100644 --- a/hippoclient/core.py +++ b/hippoclient/core.py @@ -60,7 +60,7 @@ class ClientSettings(BaseSettings): @classmethod def settings_customise_sources( cls, - settings_cls: BaseSettings, + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, diff --git a/tests/test_client/conftest.py b/tests/test_client/conftest.py index b06bd1c..1721f3c 100644 --- a/tests/test_client/conftest.py +++ b/tests/test_client/conftest.py @@ -6,6 +6,7 @@ import pytest +from henry.core import ClientSettings, Henry from hippoclient.caching import Cache @@ -19,3 +20,10 @@ def cache(tmp_path): for id in cache.complete_id_list: cache._remove(id) + + +@pytest.fixture +def client(server): + client = Henry(settings=ClientSettings(host=server["url"], verbose=True)) + + yield client diff --git a/tests/test_client/test_collection.py b/tests/test_client/test_collection.py new file mode 100644 index 0000000..15843f2 --- /dev/null +++ b/tests/test_client/test_collection.py @@ -0,0 +1,53 @@ +""" +Tests for collection uploads. +""" + +from pytest import fixture + +from henry import Henry +from hippoclient.collections import delete as delete_collection +from hippoclient.product import delete as delete_product +from hippometa import SimpleMetadata + + +@fixture +def collection_id(client: Henry): + collection = client.new_collection( + name="Test Collection A", description="A test collection for you to use!" + ) + + client.push(collection) + + yield collection.collection_id + + delete_collection(id=str(collection.collection_id), client=client.client) + + +def test_create_collection(collection_id): + print(collection_id) + + +def test_add_product_to_existing_collection(collection_id, client: Henry): + coll = client.pull_collection( + collection_id, realize_sources=False, pull_children=True + ) + + product = client.new_product( + name="Test Product", + description="Test product to be added as a collection member after the fact", + metadata=SimpleMetadata(), + sources={}, + ) + + coll.append(product) + client.push(coll) + + product_id = str(product.product_id) + + re_read_coll = client.pull_collection( + collection_id, realize_sources=True, pull_children=True + ) + + assert re_read_coll.products[0].product_id == product_id + + delete_product(client=client.client, id=product_id) From 7a865de0bd167715a995502713a1d62b0c5d5bb7 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Tue, 6 Jan 2026 10:49:38 -0500 Subject: [PATCH 4/4] Rev version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index db9d090..022d39c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ hipposerve = ["web/static/*.svg", "web/static/*.ico", "web/static/*.png", "web/t [project] name = "pyhippo" description = "The Hierarchical Product Post Office, developed for the Simons Observatory." -version = "0.2.9" +version = "0.2.10" readme = "README.md" requires-python = ">=3.11" dependencies = [