def _create_operation_method(factory_self, method_name, op_data): # Determine the correct name for the method. # This is because the method names will be standardized across # resources, so we'll have to lean on the ``api_name`` to figure out # what the correct underlying method name on the ``Connection`` should # be. # Map -> map -> unmap -> remap -> map :/ conn_method_name = to_snake_case(op_data['api_name']) if not six.PY3: method_name = str(method_name) def _new_method(self, **kwargs): params = self.full_update_params(method_name, kwargs) method = getattr(self._connection, conn_method_name, None) if not method: msg = "Introspected method named '{0}' not available on " + \ "the connection." raise NoSuchMethod(msg.format(conn_method_name)) result = method(**params) return self.full_post_process(method_name, result) _new_method.__name__ = method_name _new_method.__doc__ = DEFAULT_DOCSTRING return _new_method
def _update_docstrings(self): """ Runs through the operation methods & updates their docstrings if necessary. If the method has the default placeholder docstring, this will replace it with the docstring from the underlying connection. """ ops = self._details.resource_data['operations'] for method_name in ops.keys(): meth = getattr(self.__class__, method_name, None) if not meth: continue if meth.__doc__ != DEFAULT_DOCSTRING: # It already has a custom docstring. Leave it alone. continue # Needs updating. So there's at least *something* vaguely useful # there, use the docstring from the underlying ``Connection`` # method. # FIXME: We need to figure out a way to make this more useful, if # possible. api_name = ops[method_name]['api_name'] conn_meth = getattr(self._connection, to_snake_case(api_name)) # We need to do detection here, because Py2 treats ``.__doc__`` # as a special read-only attribute. :/ if six.PY3: meth.__doc__ = conn_meth.__doc__ else: meth.__func__.__doc__ = conn_meth.__doc__
def post_process_get(self, result): """ Given an object with identifiers, fetches the data for that object from the service. This alters the data on the object itself & simply passes through what was received. :param result: The response data :type result: dict :returns: The unmodified response data """ if not hasattr(result, 'items'): # If it's not a dict, give up & just return whatever you get. return result # We need to possibly drill into the response & get out the data here. # Check for a result key. result_key = self._details.result_key_for('get') if not result_key: # There's no result_key. Just use the top-level data. data = result else: data = result[result_key] for key, value in data.items(): self._data[to_snake_case(key)] = value return result
def build_resource(self, data): """ Given some data, builds the correct/matching ``Resource`` subclass for the ``Collection``. Useful in things like ``create`` & ``each``. :param result: The data for an instance handed back from the API. :type result: dict :returns: A ``Resource`` subclass """ if self._res_class is None: self._res_class = self._details.session.get_resource( self._details.service_name, self._details.resource ) final_data = {} # Lightly post-process the data, to look more Pythonic. for key, value in data.items(): final_data[to_snake_case(key)] = value return self._res_class(connection=self._connection, **final_data)
def test_to_snake_case(self): self.assertEqual(to_snake_case('Create'), 'create') self.assertEqual(to_snake_case('CreateQueue'), 'create_queue') self.assertEqual(to_snake_case('ThisIsReallyLong'), 'this_is_really_long') self.assertEqual(to_snake_case('createQueue'), 'create_queue')