Source code for ziffect


"""
The ziffect module.
"""

from __future__ import unicode_literals

from effect import TypeDispatcher, Effect, sync_performer
from pyrsistent import PClass, field, PClassMeta
from six import add_metaclass, iteritems
from funcsigs import signature

__all__ = [
    'interface',
    'effects',
    'argument'
]


[docs]class argument(PClass): """ Argument type TODO(mewert): fill the rest of this in. """ type = field(type=type)
def _make_intent_from_args(args): """ Create an intent type for a given set of arguments. :param args: a dict with keys as the names of arguments and values as :class:`argument`s. :returns: A new type that can hold all of the data to call a function that has the given arguments. """ class _Intent(PClass): pass for name, arg in iteritems(args): setattr(_Intent, name, field(type=arg.type)) _PIntent = add_metaclass(PClassMeta)(_Intent) return _PIntent def _iterate_methods(interface): """ A generator to iterate over the methods of an interface. :param interface: A ziffect interface. :yields: names of methods. """ for operator_name in dir(interface): if not operator_name.startswith('_'): yield operator_name def _get_method_argspecs(interface): """ A generator to get the argspecs of methods on an interface. :param interface: The ziffect interface to inspect. :yields: tuples of method name and dictionaries that map name of argument to :class:`argument` instances. """ for method_name in _iterate_methods(interface): method = getattr(interface, method_name) sig = signature(method) args = dict( (name, arg.default) for name, arg in iteritems(sig.parameters) ) yield method_name, args def _make_intents(argspecs): """ Constructs intents for each of the argspecs passed in. :param argspecs: A dict with keys as method names, and values as dicts that map name of argument to :class:`argument` instances. :return: dict that maps method_name to intent class. """ return dict( (method_name, _make_intent_from_args(args)) for method_name, args in iteritems(argspecs) ) def _make_effect_method(intent): """ Turn an intent into a method that creates an effect. :param intent: The class for the intent. :returns Effect: An effect that describes the given intent. """ def _method(self, **kwargs): return Effect(intent(**kwargs)) return _method def _make_effects(intents): """ Creates a class that has methods that generate effects for the given intents. :param intents: dict mapping names of intents to their classes. :returns: A new class with method names equal to the keys of the input. Each method on this class will generate an Effect for use with the Effect library. """ class _Effects(object): pass for method_name, intent in iteritems(intents): method = _make_effect_method(intent) setattr(_Effects, method_name, method) return _Effects()
[docs]def interface(wrapped_class): """ Class decorator to wrap ziffect interfaces. :param wrapped_class: The class to wrap. :returns: The newly created wrapped class. """ wrapped_class._ziffect_argspecs = dict( (key, value) for key, value in _get_method_argspecs(wrapped_class) ) wrapped_class._ziffect_intents = _make_intents( wrapped_class._ziffect_argspecs) wrapped_class._ziffect_effects = _make_effects( wrapped_class._ziffect_intents) return wrapped_class
[docs]def effects(interface): """ Method to get an object that implements interface by just returning effects for each method call. :param interface: The interface for which to create a provider. :returns: A class with method names equal to the method names of the interface. Each method on this class will generate an Effect for use with the Effect library. """ return interface._ziffect_effects
def implements(interface): """ Class decorator to indicate that wrapped_class implements the interface. :param interface: The interface that is implemented by the class. :returns: decorator for the wrapped class. """ def _implements_decorator(wrapped_class): return wrapped_class return _implements_decorator def _make_performer(method, arg_keys): """ Constructs a performer for that calls a specific method. This involves unpacking the intent into keyword arguments for the method. Note that this presently does not pass the dispatcher down to the underlying method. Thus, ziffect interface implementations presently cannot perform other effects that have side effects. :param method: The underlying method to call. Should be a method bound to an object that provides a ziffect interface. :param arg_keys: Iterable of strings that are both the keyword arguments of the method and the names of the attributes of the intent. :returns: An Effect performer that calls method with the arguments in the intent. """ @sync_performer def _perform(dispatcher, intent): args = dict( (k, getattr(intent, k)) for k in arg_keys ) return method(**args) return _perform def dispatcher(interface_map): """ Creates a dispatcher for a number of interfaces. :param interface_map: A map from ziffect interface to a provider of the interface. :returns: An Effect dispatcher that will use the passed in interfaces to perform Effects that have been generated from the ``ziffect.effect(interface).method()`` implementation. """ typemap = {} for interface, provider in iteritems(interface_map): intents = interface._ziffect_intents argspecs = interface._ziffect_argspecs for method_name in _iterate_methods(interface): method = getattr(provider, method_name) intent = intents[method_name] typemap[intent] = _make_performer(method, argspecs[method_name].keys()) return TypeDispatcher(typemap)