Source code for boto3.resources.action

# 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 .params import create_request_parameters
from .response import RawHandler, ResourceHandler
from .model import Action

from boto3.docs.docstring import ActionDocstring
from boto3.utils import inject_attribute


logger = logging.getLogger(__name__)


[docs]class ServiceAction(object): """ 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)
[docs]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
[docs]class WaiterAction(object): """ 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)
[docs]class CustomModeledAction(object): """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
[docs] 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)