Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(storage): add object contexts in Python GCS SDK#17039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
f76c83e30fb5c3d96977a1d1c36ca5d59ef1ef8d815aeeedd3485e748f997423cb37806f303076681b01File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -105,6 +105,7 @@ | ||
| "name", | ||
| "retention", | ||
| "storageClass", | ||
| "contexts", | ||
| ) | ||
| _READ_LESS_THAN_SIZE = ( | ||
| "Size {:d} was specified but the file-like object only had {:d} bytes remaining." | ||
| @@ -3849,6 +3850,7 @@ def compose( | ||
| if_metageneration_match=None, | ||
| if_source_generation_match=None, | ||
| retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED, | ||
| destination_contexts=None, | ||
| delete_source_objects=None, | ||
| ): | ||
| """Concatenate source blobs into this one. | ||
| @@ -3910,6 +3912,11 @@ def compose( | ||
| to enable retries regardless of generation precondition setting. | ||
| See [Configuring Retries](https://cloud.google.com/python/docs/reference/storage/latest/retry_timeout). | ||
| :type destination_contexts: :class:`~google.cloud.storage.blob.ObjectContexts` | ||
| :param destination_contexts: | ||
| (Optional) New contexts to set for the destination object. | ||
| See: https://docs.cloud.google.com/storage/docs/use-object-contexts#manage_object_contexts_during_object_operations | ||
| :type delete_source_objects: bool | ||
| :param delete_source_objects: | ||
| (Optional) If True, the source objects will be deleted after a | ||
| @@ -3965,6 +3972,14 @@ def compose( | ||
| source_objects.append(source_object) | ||
| if destination_contexts is not None: | ||
| if isinstance(destination_contexts, ObjectContexts): | ||
| self.contexts = destination_contexts | ||
| else: | ||
| raise ValueError( | ||
| "destination_contexts must be an ObjectContexts object" | ||
| ) | ||
| request = { | ||
| "sourceObjects": source_objects, | ||
| "destination": self._properties.copy(), | ||
| @@ -4007,6 +4022,7 @@ def rewrite( | ||
| if_source_metageneration_not_match=None, | ||
| timeout=_DEFAULT_TIMEOUT, | ||
| retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED, | ||
| destination_contexts=None, | ||
| ): | ||
| """Rewrite source blob into this one. | ||
| @@ -4090,6 +4106,11 @@ def rewrite( | ||
| to enable retries regardless of generation precondition setting. | ||
| See [Configuring Retries](https://cloud.google.com/python/docs/reference/storage/latest/retry_timeout). | ||
| :type destination_contexts: :class:`~google.cloud.storage.blob.ObjectContexts` or dict | ||
| :param destination_contexts: | ||
| (Optional) New contexts to set for the destination object. | ||
| See: https://docs.cloud.google.com/storage/docs/use-object-contexts#manage_object_contexts_during_object_operations | ||
| :rtype: tuple | ||
| :returns: ``(token, bytes_rewritten, total_bytes)``, where ``token`` | ||
| is a rewrite token (``None`` if the rewrite is complete), | ||
| @@ -4135,6 +4156,14 @@ def rewrite( | ||
| if_source_metageneration_not_match=if_source_metageneration_not_match, | ||
| ) | ||
| if destination_contexts is not None: | ||
| if isinstance(destination_contexts, ObjectContexts): | ||
| self.contexts = destination_contexts | ||
| else: | ||
| raise ValueError( | ||
| "destination_contexts must be an ObjectContexts object" | ||
| ) | ||
| path = f"{source.path}/rewriteTo{self.path}" | ||
| api_response = client._post_resource( | ||
| path, | ||
| @@ -5017,6 +5046,29 @@ def retention(self): | ||
| info = self._properties.get("retention", {}) | ||
| return Retention.from_api_repr(info, self) | ||
| @property | ||
| def contexts(self): | ||
| """Retrieve the contexts for this object. | ||
| :rtype: :class:`ObjectContexts` | ||
| :returns: an instance for managing the object's contexts. | ||
| """ | ||
| info = self._properties.get("contexts", {}) | ||
| return ObjectContexts.from_api_repr(info, self) | ||
| @contexts.setter | ||
| def contexts(self, value): | ||
| """Update the contexts for this object. | ||
| :type value: :class:`ObjectContexts` or dict or None | ||
| :param value: the new contexts for the object. | ||
| """ | ||
| if value is None: | ||
| self._properties["contexts"] = None | ||
| else: | ||
| self._properties["contexts"] = value | ||
| self._patch_property("contexts", value) | ||
| @property | ||
| def soft_delete_time(self): | ||
| """If this object has been soft-deleted, returns the time at which it became soft-deleted. | ||
| @@ -5309,3 +5361,140 @@ def retention_expiration_time(self): | ||
| retention_expiration_time = self.get("retentionExpirationTime") | ||
| if retention_expiration_time is not None: | ||
| return _rfc3339_nanos_to_datetime(retention_expiration_time) | ||
| class ObjectCustomContextPayload(dict): | ||
| """Payload for a custom context. | ||
| :type value: str or ``NoneType`` | ||
| :param value: (Optional) The value of the custom context. | ||
| """ | ||
| def __init__(self, value=None): | ||
| data = {"value": value} | ||
| super(ObjectCustomContextPayload, self).__init__(data) | ||
| self._contexts = None | ||
| @property | ||
| def value(self): | ||
| """The value of the custom context. | ||
| :rtype: str or ``NoneType`` | ||
| :returns: The value of the custom context. | ||
| """ | ||
| return self.get("value") | ||
| @value.setter | ||
| def value(self, value): | ||
| self["value"] = value | ||
| if hasattr(self, "_contexts") and self._contexts and self._contexts.blob: | ||
| self._contexts.blob._patch_property("contexts", self._contexts) | ||
| @property | ||
| def create_time(self): | ||
| """Creation time of the custom context. | ||
| :rtype: :class:`datetime.datetime` or ``NoneType`` | ||
| :returns: Datetime object parsed from RFC3339 valid timestamp. | ||
| """ | ||
| create_time = self.get("createTime") | ||
| if create_time is not None: | ||
| return _rfc3339_nanos_to_datetime(create_time) | ||
| @property | ||
| def update_time(self): | ||
| """Last update time of the custom context. | ||
| :rtype: :class:`datetime.datetime` or ``NoneType`` | ||
| :returns: Datetime object parsed from RFC3339 valid timestamp. | ||
| """ | ||
| update_time = self.get("updateTime") | ||
| if update_time is not None: | ||
| return _rfc3339_nanos_to_datetime(update_time) | ||
| class ObjectContexts(dict): | ||
| """Container for an object's contexts. | ||
nidhiii-27 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| See: https://docs.cloud.google.com/storage/docs/object-contexts | ||
| :type blob: :class:`Blob` | ||
| :param blob: blob for which these contexts apply to. | ||
| :type custom: dict or ``NoneType`` | ||
| :param custom: (Optional) Custom contexts mapping. | ||
| """ | ||
| def __init__(self, blob, custom=None): | ||
| data = {} | ||
| if custom is not None: | ||
| if not isinstance(custom, dict): | ||
| raise ValueError( | ||
| "custom must be a dictionary mapping keys to ObjectCustomContextPayload instances" | ||
| ) | ||
| for payload in custom.values(): | ||
| if not isinstance(payload, ObjectCustomContextPayload): | ||
| raise ValueError( | ||
| "All values in custom must be ObjectCustomContextPayload instances" | ||
| ) | ||
| payload._contexts = self | ||
| data["custom"] = custom | ||
| super(ObjectContexts, self).__init__(data) | ||
| self._blob = blob | ||
| @classmethod | ||
| def from_api_repr(cls, resource, blob): | ||
| """Factory: construct instance from resource. | ||
| :type resource: dict | ||
| :param resource: mapping as returned from API call. | ||
| :type blob: :class:`Blob` | ||
| :param blob: Blob for which these contexts apply to. | ||
| :rtype: :class:`ObjectContexts` | ||
| :returns: ObjectContexts instance created from resource. | ||
| """ | ||
| instance = cls(blob) | ||
| custom = {} | ||
| for key, payload_resource in resource.get("custom", {}).items(): | ||
| payload = ObjectCustomContextPayload() | ||
| payload.update(payload_resource) | ||
| payload._contexts = instance | ||
| custom[key] = payload | ||
| instance["custom"] = custom | ||
| return instance | ||
| @property | ||
| def blob(self): | ||
| """Blob for which these contexts apply to. | ||
| :rtype: :class:`Blob` | ||
| :returns: the instance's blob. | ||
| """ | ||
| return self._blob | ||
| @property | ||
| def custom(self): | ||
| """Custom contexts mapping. | ||
| :rtype: dict | ||
| :returns: Mapping of keys to :class:`ObjectCustomContextPayload` instances. | ||
| """ | ||
| if "custom" not in self: | ||
| self["custom"] = {} | ||
| return self["custom"] | ||
| @custom.setter | ||
| def custom(self, value): | ||
| if value is None: | ||
| value = {} | ||
| if not isinstance(value, dict): | ||
| raise ValueError( | ||
| "custom must be a dictionary mapping keys to ObjectCustomContextPayload instances" | ||
| ) | ||
| for payload in value.values(): | ||
| if isinstance(payload, ObjectCustomContextPayload): | ||
| payload._contexts = self | ||
| self["custom"] = value | ||
nidhiii-27 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| self.blob._patch_property("contexts", self) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1291,6 +1291,7 @@ def list_blobs( | ||
| match_glob=None, | ||
| include_folders_as_prefixes=None, | ||
| soft_deleted=None, | ||
| filter_=None, | ||
| ): | ||
| """Return an iterator used to find blobs in the bucket. | ||
| @@ -1400,6 +1401,10 @@ def list_blobs( | ||
| Note ``soft_deleted`` and ``versions`` cannot be set to True simultaneously. See: | ||
| https://cloud.google.com/storage/docs/soft-delete | ||
| filter_ (str): | ||
nidhiii-27 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| (Optional) Filter string used to filter objects. See: | ||
| https://docs.cloud.google.com/storage/docs/listing-objects#filter-by-object-contexts-syntax | ||
| Returns: | ||
| Iterator of all :class:`~google.cloud.storage.blob.Blob` | ||
| in this bucket matching the arguments. The RPC call | ||
| @@ -1443,6 +1448,9 @@ def list_blobs( | ||
| if soft_deleted is not None: | ||
| extra_params["softDeleted"] = soft_deleted | ||
| if filter_ is not None: | ||
| extra_params["filter"] = filter_ | ||
| if bucket.user_project is not None: | ||
| extra_params["userProject"] = bucket.user_project | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.