Spaces:
Paused
Paused
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"). You | |
# may not use this file except in compliance with the License. A copy of | |
# the License is located at | |
# | |
# https://aws.amazon.com/apache2.0/ | |
# | |
# or in the "license" file accompanying this file. This file is | |
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | |
# ANY KIND, either express or implied. See the License for the specific | |
# language governing permissions and limitations under the License. | |
import logging | |
from botocore import xform_name | |
from boto3.docs.docstring import ActionDocstring | |
from boto3.utils import inject_attribute | |
from .model import Action | |
from .params import create_request_parameters | |
from .response import RawHandler, ResourceHandler | |
logger = logging.getLogger(__name__) | |
class ServiceAction: | |
""" | |
A class representing a callable action on a resource, for example | |
``sqs.get_queue_by_name(...)`` or ``s3.Bucket('foo').delete()``. | |
The action may construct parameters from existing resource identifiers | |
and may return either a raw response or a new resource instance. | |
:type action_model: :py:class`~boto3.resources.model.Action` | |
:param action_model: The action model. | |
:type factory: ResourceFactory | |
:param factory: The factory that created the resource class to which | |
this action is attached. | |
:type service_context: :py:class:`~boto3.utils.ServiceContext` | |
:param service_context: Context about the AWS service | |
""" | |
def __init__(self, action_model, factory=None, service_context=None): | |
self._action_model = action_model | |
# In the simplest case we just return the response, but if a | |
# resource is defined, then we must create these before returning. | |
resource_response_model = action_model.resource | |
if resource_response_model: | |
self._response_handler = ResourceHandler( | |
search_path=resource_response_model.path, | |
factory=factory, | |
resource_model=resource_response_model, | |
service_context=service_context, | |
operation_name=action_model.request.operation, | |
) | |
else: | |
self._response_handler = RawHandler(action_model.path) | |
def __call__(self, parent, *args, **kwargs): | |
""" | |
Perform the action's request operation after building operation | |
parameters and build any defined resources from the response. | |
:type parent: :py:class:`~boto3.resources.base.ServiceResource` | |
:param parent: The resource instance to which this action is attached. | |
:rtype: dict or ServiceResource or list(ServiceResource) | |
:return: The response, either as a raw dict or resource instance(s). | |
""" | |
operation_name = xform_name(self._action_model.request.operation) | |
# First, build predefined params and then update with the | |
# user-supplied kwargs, which allows overriding the pre-built | |
# params if needed. | |
params = create_request_parameters(parent, self._action_model.request) | |
params.update(kwargs) | |
logger.debug( | |
'Calling %s:%s with %r', | |
parent.meta.service_name, | |
operation_name, | |
params, | |
) | |
response = getattr(parent.meta.client, operation_name)(*args, **params) | |
logger.debug('Response: %r', response) | |
return self._response_handler(parent, params, response) | |
class BatchAction(ServiceAction): | |
""" | |
An action which operates on a batch of items in a collection, typically | |
a single page of results from the collection's underlying service | |
operation call. For example, this allows you to delete up to 999 | |
S3 objects in a single operation rather than calling ``.delete()`` on | |
each one individually. | |
:type action_model: :py:class`~boto3.resources.model.Action` | |
:param action_model: The action model. | |
:type factory: ResourceFactory | |
:param factory: The factory that created the resource class to which | |
this action is attached. | |
:type service_context: :py:class:`~boto3.utils.ServiceContext` | |
:param service_context: Context about the AWS service | |
""" | |
def __call__(self, parent, *args, **kwargs): | |
""" | |
Perform the batch action's operation on every page of results | |
from the collection. | |
:type parent: | |
:py:class:`~boto3.resources.collection.ResourceCollection` | |
:param parent: The collection iterator to which this action | |
is attached. | |
:rtype: list(dict) | |
:return: A list of low-level response dicts from each call. | |
""" | |
service_name = None | |
client = None | |
responses = [] | |
operation_name = xform_name(self._action_model.request.operation) | |
# Unlike the simple action above, a batch action must operate | |
# on batches (or pages) of items. So we get each page, construct | |
# the necessary parameters and call the batch operation. | |
for page in parent.pages(): | |
params = {} | |
for index, resource in enumerate(page): | |
# There is no public interface to get a service name | |
# or low-level client from a collection, so we get | |
# these from the first resource in the collection. | |
if service_name is None: | |
service_name = resource.meta.service_name | |
if client is None: | |
client = resource.meta.client | |
create_request_parameters( | |
resource, | |
self._action_model.request, | |
params=params, | |
index=index, | |
) | |
if not params: | |
# There are no items, no need to make a call. | |
break | |
params.update(kwargs) | |
logger.debug( | |
'Calling %s:%s with %r', service_name, operation_name, params | |
) | |
response = getattr(client, operation_name)(*args, **params) | |
logger.debug('Response: %r', response) | |
responses.append(self._response_handler(parent, params, response)) | |
return responses | |
class WaiterAction: | |
""" | |
A class representing a callable waiter action on a resource, for example | |
``s3.Bucket('foo').wait_until_bucket_exists()``. | |
The waiter action may construct parameters from existing resource | |
identifiers. | |
:type waiter_model: :py:class`~boto3.resources.model.Waiter` | |
:param waiter_model: The action waiter. | |
:type waiter_resource_name: string | |
:param waiter_resource_name: The name of the waiter action for the | |
resource. It usually begins with a | |
``wait_until_`` | |
""" | |
def __init__(self, waiter_model, waiter_resource_name): | |
self._waiter_model = waiter_model | |
self._waiter_resource_name = waiter_resource_name | |
def __call__(self, parent, *args, **kwargs): | |
""" | |
Perform the wait operation after building operation | |
parameters. | |
:type parent: :py:class:`~boto3.resources.base.ServiceResource` | |
:param parent: The resource instance to which this action is attached. | |
""" | |
client_waiter_name = xform_name(self._waiter_model.waiter_name) | |
# First, build predefined params and then update with the | |
# user-supplied kwargs, which allows overriding the pre-built | |
# params if needed. | |
params = create_request_parameters(parent, self._waiter_model) | |
params.update(kwargs) | |
logger.debug( | |
'Calling %s:%s with %r', | |
parent.meta.service_name, | |
self._waiter_resource_name, | |
params, | |
) | |
client = parent.meta.client | |
waiter = client.get_waiter(client_waiter_name) | |
response = waiter.wait(**params) | |
logger.debug('Response: %r', response) | |
class CustomModeledAction: | |
"""A custom, modeled action to inject into a resource.""" | |
def __init__(self, action_name, action_model, function, event_emitter): | |
""" | |
:type action_name: str | |
:param action_name: The name of the action to inject, e.g. | |
'delete_tags' | |
:type action_model: dict | |
:param action_model: A JSON definition of the action, as if it were | |
part of the resource model. | |
:type function: function | |
:param function: The function to perform when the action is called. | |
The first argument should be 'self', which will be the resource | |
the function is to be called on. | |
:type event_emitter: :py:class:`botocore.hooks.BaseEventHooks` | |
:param event_emitter: The session event emitter. | |
""" | |
self.name = action_name | |
self.model = action_model | |
self.function = function | |
self.emitter = event_emitter | |
def inject(self, class_attributes, service_context, event_name, **kwargs): | |
resource_name = event_name.rsplit(".")[-1] | |
action = Action(self.name, self.model, {}) | |
self.function.__name__ = self.name | |
self.function.__doc__ = ActionDocstring( | |
resource_name=resource_name, | |
event_emitter=self.emitter, | |
action_model=action, | |
service_model=service_context.service_model, | |
include_signature=False, | |
) | |
inject_attribute(class_attributes, self.name, self.function) | |