Skip to content

Commit

Permalink
Merge branch 'release-0.0.10'
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Mar 5, 2015
2 parents f45e9cb + b270599 commit 4d3330e
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 127 deletions.
27 changes: 25 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
Changelog
=========

Unreleased
----------
0.0.10 - 2015-03-05
-------------------

* bugfix:Documentation: Name collisions are now handled at the resource
model layer instead of the factory, meaning that the documentation
now uses the correct names.
(`issue 67 <https://github.com/boto/boto3/pull/67>`__)
* feature:Session: Add a ``region_name`` option when creating a session.
(`issue 69 <https://github.com/boto/boto3/pull/69>`__,
`issue 21 <https://github.com/boto/boto3/issues/21>`__)
* feature:Botocore: Update to Botocore 0.94.0

* Update to the latest Amazon CloudeSearch API.
* Add support for near-realtime data updates and exporting historical
data from Amazon Cognito Sync.
* **Removed** the ability to clone a low-level client. Instead, create
a new client with the same parameters.
* Add support for URL paths in an endpoint URL.
* Multithreading signature fixes.
* Add support for listing hosted zones by name and getting hosted zone
counts from Amazon Route53.
* Add support for tagging to AWS Data Pipeline.

0.0.9 - 2015-02-19
------------------

* feature:Botocore: Update to Botocore 0.92.0

Expand Down
2 changes: 1 addition & 1 deletion boto3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


__author__ = 'Amazon Web Services'
__version__ = '0.0.9'
__version__ = '0.0.10'


# The default Boto3 session; autoloaded when needed.
Expand Down
15 changes: 11 additions & 4 deletions boto3/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ def docs_for(service_name):
for name, model in sorted(data['resources'].items(),
key=lambda i:i[0]):
resource_model = ResourceModel(name, model, data['resources'])

shape = None
if resource_model.shape:
shape = service_model.shape_for(resource_model.shape)
resource_model.load_rename_map(shape)

if name not in models:
models[name] = {'type': 'resource', 'model': resource_model}

Expand Down Expand Up @@ -333,7 +339,8 @@ def document_resource(service_name, official_name, resource_model,
docs += ' Attributes:\n\n'
shape = service_model.shape_for(resource_model.shape)

for name, member in sorted(shape.members.items()):
attributes = resource_model.get_attributes(shape)
for name, (orig_name, member) in sorted(attributes.items()):
docs += (' .. py:attribute:: {0}\n\n (``{1}``)'
' {2}\n\n').format(
xform_name(name), py_type_name(member.type_name),
Expand Down Expand Up @@ -403,7 +410,7 @@ def document_resource(service_name, official_name, resource_model,
' to reach a specific state.\n\n')
service_waiter_model = session.get_waiter_model(service_name)
for waiter in sorted(resource_model.waiters,
key=lambda i: i.resource_waiter_name):
key=lambda i: i.name):
docs += document_waiter(waiter, service_name, resource_model,
service_model, service_waiter_model)

Expand Down Expand Up @@ -467,7 +474,7 @@ def document_waiter(waiter, service_name, resource_model, service_model,
' This method calls ``wait()`` on'
' :py:meth:`{2}.Client.get_waiter` using `{3}`_ .').format(
resource_model.name,
xform_name(waiter.name).replace('_', ' '),
' '.join(waiter.name.split('_')[2:]),
service_name,
xform_name(waiter.waiter_name))

Expand All @@ -476,7 +483,7 @@ def document_waiter(waiter, service_name, resource_model, service_model,

return document_operation(
operation_model=operation_model, service_name=service_name,
operation_name=xform_name(waiter.resource_waiter_name),
operation_name=xform_name(waiter.name),
description=description,
example_instance = xform_name(resource_model.name),
ignore_params=ignore_params, rtype=None)
Expand Down
114 changes: 29 additions & 85 deletions boto3/resources/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import logging
from functools import partial

from botocore import xform_name

from .action import ServiceAction
from .action import WaiterAction
from .base import ResourceMeta, ServiceResource
Expand Down Expand Up @@ -74,6 +72,11 @@ def load_from_definition(self, service_name, resource_name, model,

resource_model = ResourceModel(resource_name, model, resource_defs)

shape = None
if resource_model.shape:
shape = service_model.shape_for(resource_model.shape)
resource_model.load_rename_map(shape)

self._load_identifiers(attrs, meta, resource_model)
self._load_actions(attrs, resource_model, resource_defs,
service_model)
Expand All @@ -98,11 +101,8 @@ def _load_identifiers(self, attrs, meta, model):
operations on the resource.
"""
for identifier in model.identifiers:
snake_cased = xform_name(identifier.name)
snake_cased = self._check_allowed_name(
attrs, snake_cased, 'identifier', model.name)
meta.identifiers.append(snake_cased)
attrs[snake_cased] = None
meta.identifiers.append(identifier.name)
attrs[identifier.name] = None

def _load_actions(self, attrs, model, resource_defs, service_model):
"""
Expand All @@ -112,16 +112,12 @@ def _load_actions(self, attrs, model, resource_defs, service_model):
"""
if model.load:
attrs['load'] = self._create_action(
'load', model.load, resource_defs, service_model,
is_load=True)
model.load, resource_defs, service_model, is_load=True)
attrs['reload'] = attrs['load']

for action in model.actions:
snake_cased = xform_name(action.name)
snake_cased = self._check_allowed_name(
attrs, snake_cased, 'action', model.name)
attrs[snake_cased] = self._create_action(snake_cased,
action, resource_defs, service_model)
attrs[action.name] = self._create_action(action, resource_defs,
service_model)

def _load_attributes(self, attrs, meta, model, service_model):
"""
Expand All @@ -133,16 +129,9 @@ def _load_attributes(self, attrs, meta, model, service_model):
if model.shape:
shape = service_model.shape_for(model.shape)

for name, member in shape.members.items():
snake_cased = xform_name(name)
if snake_cased in meta.identifiers:
# Skip identifiers, these are set through other means
continue

snake_cased = self._check_allowed_name(
attrs, snake_cased, 'attribute', model.name)
attrs[snake_cased] = self._create_autoload_property(name,
snake_cased)
attributes = model.get_attributes(shape)
for name, (orig_name, member) in attributes.items():
attrs[name] = self._create_autoload_property(orig_name, name)

def _load_collections(self, attrs, model, resource_defs, service_model):
"""
Expand All @@ -152,12 +141,8 @@ def _load_collections(self, attrs, model, resource_defs, service_model):
through the collection's items.
"""
for collection_model in model.collections:
snake_cased = xform_name(collection_model.name)
snake_cased = self._check_allowed_name(
attrs, snake_cased, 'collection', model.name)

attrs[snake_cased] = self._create_collection(
attrs['meta'].service_name, model.name, snake_cased,
attrs[collection_model.name] = self._create_collection(
attrs['meta'].service_name, model.name,
collection_model, resource_defs, service_model)

def _load_has_relations(self, attrs, service_name, resource_name,
Expand All @@ -176,11 +161,8 @@ def _load_has_relations(self, attrs, service_name, resource_name,
# This is a dangling reference, i.e. we have all
# the data we need to create the resource, so
# this instance becomes an attribute on the class.
snake_cased = xform_name(reference.name)
snake_cased = self._check_allowed_name(
attrs, snake_cased, 'reference', model.name)
attrs[snake_cased] = self._create_reference(
reference.resource.type, snake_cased, reference,
attrs[reference.name] = self._create_reference(
reference.resource.type, reference,
service_name, resource_name, model, resource_defs,
service_model)

Expand All @@ -200,44 +182,7 @@ def _load_waiters(self, attrs, model):
of the resource.
"""
for waiter in model.waiters:
snake_cased = xform_name(waiter.resource_waiter_name)
snake_cased = self._check_allowed_name(
attrs, snake_cased, 'waiter', model.name)
attrs[snake_cased] = self._create_waiter(waiter, snake_cased)

def _check_allowed_name(self, attrs, name, category, resource_name):
"""
Determine if a given name is allowed on the instance, and if not,
then raise an exception. This prevents public attributes of the
class from being clobbered, e.g. since we define ``Resource.meta``,
no identifier may be named ``meta``. Another example: no action
named ``queue_items`` may be added after an identifier of the same
name has been added.
One attempt is made in the event of a collision to remedy the
situation. The ``category`` is appended to the name and the
check is performed again. For example, if an action named
``get_frobs`` fails the test, then we try ``get_frobs_action``
after logging a warning.
:raises: ValueError
"""
if name in attrs:
logger.warning('%s `%s` would clobber existing %s'
' resource attribute, going to try'
' %s instead...', category, name,
resource_name, name + '_' + category)
# TODO: Move this logic into the model and strictly
# define the loading order of categories. This
# will make documentation much simpler.
name = name + '_' + category

if name in attrs:
raise ValueError('{0} `{1}` would clobber existing '
'{2} resource attribute'.format(
category, name, resource_name))

return name
attrs[waiter.name] = self._create_waiter(waiter)

def _create_autoload_property(factory_self, name, snake_cased):
"""
Expand All @@ -262,22 +207,22 @@ def property_loader(self):
property_loader.__doc__ = 'TODO'
return property(property_loader)

def _create_waiter(factory_self, waiter_model, snake_cased):
def _create_waiter(factory_self, waiter_model):
"""
Creates a new wait method for each resource where both a waiter and
resource model is defined.
"""
waiter = WaiterAction(waiter_model, waiter_resource_name=snake_cased)
waiter = WaiterAction(waiter_model,
waiter_resource_name=waiter_model.name)
def do_waiter(self, *args, **kwargs):
waiter(self, *args, **kwargs)

do_waiter.__name__ = str(snake_cased)
do_waiter.__name__ = str(waiter_model.name)
do_waiter.__doc__ = 'TODO'
return do_waiter

def _create_collection(factory_self, service_name, resource_name,
snake_cased, collection_model,
resource_defs, service_model):
collection_model, resource_defs, service_model):
"""
Creates a new property on the resource to lazy-load a collection.
"""
Expand All @@ -289,13 +234,12 @@ def get_collection(self):
return cls(collection_model, self, factory_self,
resource_defs, service_model)

get_collection.__name__ = str(snake_cased)
get_collection.__name__ = str(collection_model.name)
get_collection.__doc__ = 'TODO'
return property(get_collection)

def _create_reference(factory_self, name, snake_cased, reference,
service_name, resource_name, model, resource_defs,
service_model):
def _create_reference(factory_self, name, reference, service_name,
resource_name, model, resource_defs, service_model):
"""
Creates a new property on the resource to lazy-load a reference.
"""
Expand All @@ -313,7 +257,7 @@ def get_reference(self):
# identifiers to instantiate the resource reference.
return handler(self, {}, {})

get_reference.__name__ = str(snake_cased)
get_reference.__name__ = str(reference.name)
get_reference.__doc__ = 'TODO'
return property(get_reference)

Expand Down Expand Up @@ -352,7 +296,7 @@ def create_resource(self, *args, **kwargs):
create_resource.__doc__ = 'TODO'
return create_resource

def _create_action(factory_self, snake_cased, action_model, resource_defs,
def _create_action(factory_self, action_model, resource_defs,
service_model, is_load=False):
"""
Creates a new method which makes a request to the underlying
Expand Down Expand Up @@ -386,6 +330,6 @@ def do_action(self, *args, **kwargs):

return response

do_action.__name__ = str(snake_cased)
do_action.__name__ = str(action_model.name)
do_action.__doc__ = 'TODO'
return do_action
Loading

0 comments on commit 4d3330e

Please sign in to comment.