From c413b71312eee10035c64d62b293a6fb91346821 Mon Sep 17 00:00:00 2001 From: "Stephen C. Pope" Date: Wed, 17 Jan 2024 15:46:50 -0700 Subject: [PATCH] [Core-251] Client: Add Blob.get_or_create method (#12399) GitOrigin-RevId: 8eaa635aeb54ebce96d437dfee276c9eda7313e9 --- README.md | 3 ++ descarteslabs/core/catalog/blob.py | 61 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/README.md b/README.md index 3943389e..461531d1 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ Changelog - *Breaking Change*: Derived bands, never supported in the AWS environment and catalog products, have been removed. - The new `Blob.delete_many` method may be used to delete large numbers of blobs efficiently. +- The `Blob.get_or_create` method didn't allow supplying `storage_type`, `namespace`, or `name` parameters. + Now it works as expected, either returning a saved Blob from the Catalog, or an unsaved blob that + you can use to upload and save its data. ### Compute diff --git a/descarteslabs/core/catalog/blob.py b/descarteslabs/core/catalog/blob.py index a077be30..47a1744a 100644 --- a/descarteslabs/core/catalog/blob.py +++ b/descarteslabs/core/catalog/blob.py @@ -396,6 +396,67 @@ def get( id = f"{storage_type}/{Blob.namespace_id(namespace)}/{name}" return super(cls, Blob).get(id) + @classmethod + def get_or_create( + cls, + id=None, + storage_type=StorageType.DATA, + namespace=None, + name=None, + client=None, + **kwargs, + ): + """Get an existing object from the Descartes Labs catalog or create a new object. + + If the Descartes Labs catalog object is found, and the remainder of the + arguments do not differ from the values in the retrieved instance, it will be + returned in the `~descarteslabs.catalog.DocumentState.SAVED` state. + + If the Descartes Labs catalog object is found, and the remainder of the + arguments update one or more values in the instance, it will be returned in + the `~descarteslabs.catalog.DocumentState.MODIFIED` state. + + If the Descartes Labs catalog object is not found, it will be created and the + state will be `~descarteslabs.catalog.DocumentState.UNSAVED`. Also see the + example for :py:meth:`save`. + + Parameters + ---------- + id : str, optional + The id of the object you are requesting. Required unless ``name`` is supplied. + May not be specified if ``name`` is specified. + storage_type : StorageType, optional + The storage type of the Blob you wish to retrieve. Defaults to ``data``. Ignored + unless ``name`` is specified. + namespace : str, optional + The namespace of the Blob you wish to retrieve. Defaults to the user's org name + (if any) plus the unique user hash. Ignored unless ``name`` is specified. + name : str, optional + The name of the Blob you wish to retrieve. Required if ``id`` is not specified. + May not be specified if ``id`` is specified. + client : CatalogClient, optional + A `CatalogClient` instance to use for requests to the Descartes Labs + catalog. The + :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will + be used if not set. + kwargs : dict, optional + With the exception of readonly attributes (`created`, `modified`), any + attribute of a catalog object can be set as a keyword argument (Also see + `ATTRIBUTES`). + + Returns + ------- + :py:class:`~descarteslabs.catalog.CatalogObject` + The requested catalog object that was retrieved or created. + + """ + if (not id and not name) or (id and name): + raise TypeError("Must specify exactly one of id or name parameters") + if not id: + id = f"{storage_type}/{Blob.namespace_id(namespace)}/{name}" + + return super(cls, Blob).get_or_create(id, client=client, **kwargs) + @classmethod def search(cls, client=None, request_params=None): """A search query for all blobs.