From 6b338cb70192f226f013d58321754480db953ea6 Mon Sep 17 00:00:00 2001 From: "Stephen C. Pope" Date: Sat, 19 Oct 2024 14:27:07 -0600 Subject: [PATCH] [Core-547] Docs: Update Catalog guide for new event support (#12709) GitOrigin-RevId: 95d6f58b96ac35e4c21915804dfc29c4cce68ee2 --- descarteslabs/core/catalog/blob.py | 5 +- descarteslabs/core/catalog/catalog_base.py | 48 +++++++++++++------ .../core/catalog/event_subscription.py | 10 ++-- descarteslabs/core/catalog/image_upload.py | 4 +- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/descarteslabs/core/catalog/blob.py b/descarteslabs/core/catalog/blob.py index 8094e144..a090e3a8 100644 --- a/descarteslabs/core/catalog/blob.py +++ b/descarteslabs/core/catalog/blob.py @@ -36,7 +36,6 @@ CatalogClient, CatalogObject, check_deleted, - check_derived, UnsavedObjectError, ) from .search import AggregateDateField, GeoSearch, SummarySearchMixin @@ -1022,8 +1021,7 @@ def _do_download(self, dest=None, range=None): r.close() @classmethod - @check_derived - def delete(cls, id, client=None): + def _cls_delete(cls, id, client=None): """Delete the catalog object with the given `id`. Parameters @@ -1064,7 +1062,6 @@ def delete(cls, id, client=None): except NotFoundError: return None - @check_deleted def _instance_delete(self): """Delete this catalog object from the Descartes Labs catalog. diff --git a/descarteslabs/core/catalog/catalog_base.py b/descarteslabs/core/catalog/catalog_base.py index a092ebf7..9a5910b2 100644 --- a/descarteslabs/core/catalog/catalog_base.py +++ b/descarteslabs/core/catalog/catalog_base.py @@ -97,6 +97,30 @@ def _new_abstract_class(cls, abstract_cls): return super(abstract_cls, cls).__new__(cls) +# This lets us have a class method and an instance method with the same name, but +# different signatures and implementation. +# see https://stackoverflow.com/questions/28237955/same-name-for-classmethod-and-instancemethod +class hybridmethod: + def __init__(self, fclass, finstance=None, doc=None): + self.fclass = fclass + self.finstance = finstance + self.__doc__ = doc or fclass.__doc__ + # support use on abstract base classes + self.__isabstractmethod__ = bool(getattr(fclass, "__isabstractmethod__", False)) + + def classmethod(self, fclass): + return type(self)(fclass, self.finstance, None) + + def instancemethod(self, finstance): + return type(self)(self.fclass, finstance, self.__doc__) + + def __get__(self, instance, cls): + if instance is None or self.finstance is None: + # either bound to the class, or no instance method available + return self.fclass.__get__(cls, None) + return self.finstance.__get__(instance, cls) + + class CatalogObjectMeta(AttributeMeta): def __new__(cls, name, bases, attrs): new_cls = super(CatalogObjectMeta, cls).__new__(cls, name, bases, attrs) @@ -106,17 +130,6 @@ def __new__(cls, name, bases, attrs): (new_cls._doc_type, new_cls._derived_type) ] = new_cls - if new_cls.__doc__ is not None and new_cls._instance_delete.__doc__ is not None: - # Careful with this; leading white space is very significant - new_cls.__doc__ += ( - """ - Methods - ------- - delete() - """ - + new_cls._instance_delete.__doc__ - ) - return new_cls @@ -182,7 +195,6 @@ def __new__(cls, *args, **kwargs): return _new_abstract_class(cls, CatalogObjectBase) def __init__(self, **kwargs): - self.delete = self._instance_delete self._client = kwargs.pop("client", None) or CatalogClient.get_default_client() self._attributes = {} @@ -865,7 +877,7 @@ def reload(self, request_params=None, headers=None): **data["attributes"], ) - @classmethod + @hybridmethod @check_derived def delete(cls, id, client=None): """Delete the catalog object with the given `id`. @@ -898,6 +910,10 @@ def delete(cls, id, client=None): ------- >>> Image.delete('my-image-id') # doctest: +SKIP """ + return cls._cls_delete(id, client=client) + + @classmethod + def _cls_delete(cls, id, client=None): if client is None: client = CatalogClient.get_default_client() @@ -907,8 +923,9 @@ def delete(cls, id, client=None): except NotFoundError: return False + @delete.instancemethod @check_deleted - def _instance_delete(self): + def delete(self): """Delete this catalog object from the Descartes Labs catalog. Once deleted, you cannot use the catalog object and should release any @@ -924,6 +941,9 @@ def _instance_delete(self): :ref:`Spurious exception ` that can occur during a network request. """ + return self._instance_delete() + + def _instance_delete(self): if self.state == DocumentState.UNSAVED: raise UnsavedObjectError("You cannot delete an unsaved object.") diff --git a/descarteslabs/core/catalog/event_subscription.py b/descarteslabs/core/catalog/event_subscription.py index 2a02d376..05d91b77 100644 --- a/descarteslabs/core/catalog/event_subscription.py +++ b/descarteslabs/core/catalog/event_subscription.py @@ -755,7 +755,7 @@ def __init__(self, *product_ids, **kwargs): Parameters ---------- - product_ids : str (one or more positional arguments) + product_ids : str, as one or more positional arguments The ids of one or more products to be subscribed, as separate positional arguments. Plus any additional keyword arguments to pass to the EventSubscription constructor. """ @@ -789,7 +789,7 @@ def __init__(self, *namespaces, **kwargs): Parameters ---------- - namespaces : str (one or more positional arguments) + namespaces : str, as one or more positional arguments One or more storage namespaces to be subscribed, as separate positional arguments. Plus any additional keyword arguments to pass to the EventSubscription constructor. """ @@ -823,7 +823,7 @@ def __init__(self, *product_ids, **kwargs): Parameters ---------- - product_ids : str (one or more positional arguments) + product_ids : str, as one or more positional arguments The ids of one or more vector products to be subscribed, as separate positional arguments. Plus any additional keyword arguments to pass to the EventSubscription constructor. """ @@ -859,7 +859,7 @@ def __init__(self, *function_ids, **kwargs): Parameters ---------- - function_ids : str (one or more positional arguments) + function_ids : str, as one or more positional arguments One or more Function ids or Function namespaces to be subscribed, as separate positional arguments. A Function namespace will match all functions in that namespace. @@ -895,7 +895,7 @@ def __init__(self, *event_schedule_ids, **kwargs): Parameters ---------- - event_schedule_ids : str (one or more positional arguments) + event_schedule_ids : str, as one or more positional arguments The ids of one or more scheduled event to be subscribed, as separate positional arguments. Plus any additional keyword arguments to pass to the EventSubscription constructor. """ diff --git a/descarteslabs/core/catalog/image_upload.py b/descarteslabs/core/catalog/image_upload.py index 20e0b1e9..918d8d9e 100644 --- a/descarteslabs/core/catalog/image_upload.py +++ b/descarteslabs/core/catalog/image_upload.py @@ -542,7 +542,7 @@ def _load_related_objects(cls, response, client): return related_objects @classmethod - def delete(cls, id, client=None, ignore_missing=False): + def _cls_delete(cls, id, client=None): """You cannot delete an ImageUpload. Raises @@ -552,5 +552,5 @@ def delete(cls, id, client=None, ignore_missing=False): """ raise NotImplementedError("Deleting ImageUploads is not permitted") - def _instance_delete(self, ignore_missing=False): + def _instance_delete(self): raise NotImplementedError("Deleting ImageUploads is not permitted")