def any_of(*cls, **kwargs): '''Creates an ``Action`` subclass that passes requests to one of the given classes. The returned subclass is a dynamically created subclass of :class:`AnyAction` called ``AnyAction_<hash value>``, where ``<hash value>`` is a deterministic function of the arguments provided to this function. It delegates calls to :meth:`~Action.handles()` to its constituent classes (the parameters given in ``cls``) such that it accepts the request if *any* of the constituent classes individually accept the request. Instances of ``AnyAction`` are never actually created; when you construct an ``AnyAction`` for a given request, what you actually get is an instance of the first constituent class which has agreed to handle the request. If one of the parameters to this method is itself a subclass of ``AnyAction``, its list of constituent classes, rather than the parameter class itself, will be copied into the new AnyAction. This means that, for efficiency, ``AnyAction`` will never be nested.''' handler_classes = [] for n in cls: if not issubclass(n, Action): return NotImplemented if issubclass(n, AnyAction): handler_classes.extend(n.handler_classes) else: handler_classes.append([0,n]) return type('AnyAction_%s' % hash_iterable(handler_classes), (AnyAction,), {'handler_classes': handler_classes, '_count': 8, '_sortable': kwargs.get('sortable', False)})
def all_of(*cls): '''Creates an ``Action`` subclass that passes requests to all given classes. The returned subclass is a dynamically created subclass of :class:`AllActions` called ``AllActions_<hash value>``, where ``<hash value>`` is a deterministic function of the arguments provided to this function. The subclass delegates calls to :meth:`~Action.handles` to its constituent classes (the parameters given in ``cls``) such that the ``AllActions`` subclass only accepts the request if *all* its constituent classes individually accept the request. When an instance of ``AllActions_<hash value>`` is created in the second phase of processing, it internally also creates instances of all its constituent classes to which it will delegate calls to instance methods like :meth:`update_mtime` and :meth:`generate`. If one of the parameters to this method is itself a subclass of ``AllActions``, its list of constituent classes, rather than the parameter class itself, will be copied into the new ``AllActions``. This reduces the total number of instances of ``AllActions`` necessary.''' handler_classes = [] for n in cls: if not issubclass(n, Action): return NotImplemented if issubclass(n, AllActions): handler_classes.extend(n.handler_classes) else: handler_classes.append(n) # Use the metaclass to create a dynamic subclass of AllActions # with our list of handler classes. return ActionMetaclass('AllActions_%s' % hash_iterable(handler_classes), (AllActions,), {'handler_classes': handler_classes})
def derive(cls, **kwargs): '''Returns a subclass of this class with selected class variables set. You can think of ``derive()`` as a constructor of sorts. Normally, of course, a constructor takes a class and some values and creates an instance of the class based on those values. This method works similarly, except that instead of creating an instance, it creates a subclass. By default, each keyword argument passed to ``derive()`` will be copied over to a corresponding class variable of that subclass. For example, ``Action.derive(foo='bar')`` returns a subclass of ``Action`` with a class variable ``foo`` that has a default value of ``bar``. Another way to get the same effect would be :: class RandomActionSubclass(Action): foo = 'bar' Some subclasses of Action override ``derive()`` to do something more complicated with the given values, but they generally wind up as class variables in some form. The subclass returned by ``derive()`` will have a name of the form ``<class name>_<hash value>``, where ``<class name>` is the name of the base class, and ``<hash value>`` is some deterministic function of the keys and values passed in as keyword arguments. (The specific hash function used is purposely undocumented and may change; in practice it should probably never be necessary to know the hash generated.) If you write a subclass of Action that requires this sort of customization, and you don't have default values for the custom class variables, you can override ``derive()`` as follows to specify which properties your class requires:: def derive(cls, <property1>, <property2>, ..., **kwargs): return super(<class>, cls).derive(<property1>=<property1>, <property2>=<property2>, ..., **kwargs) Just replace ``property1``, ``property2``, etc. in all three spots with the name of each property, and ``<class>`` with the name of the class. Make sure to leave the ``**kwargs`` in at the end, because your class might be subclassed yet again and the subsubclass might want to have additional custom properties. It's boilerplate code but I haven't found a way to reduce it further than this. If you have sensible defaults for all your properties, you can just set those defaults as class variables and then you don't have to override ``derive()`` at all. In any case, you really should document which variables ``derive()`` accepts or requires for your class, if any.''' return type('%s_%s' % (cls.__name__, hash_iterable(kwargs)), (cls,), kwargs)
def opt(cls): '''Creates an Action subclass that wraps a given handler class to make it optional. The returned subclass is called OptAction_<hash value>, where <hash value> is a deterministic function of the argument. It returns True from handles(req) for all requests, but if its constituent class (the parameter given in cls) doesn't actually handle the request, attempting to create an instance of OptAction returns a NoopHandler (which does nothing) instead. If the constituent class does handle the request, than an instance of it itself is created and returned. If the parameter to this method is itself a subclass of OptAction, then it will be returned itself, rather than a new OptAction being created to wrap it.''' if not issubclass(cls, Action): return NotImplemented elif cls.__name__.startswith('OptAction'): return cls else: return type('OptAction_%s' % hash_iterable([cls]), (OptAction,), {'handler_class': cls})
def action_id(self): return hash_iterable(filter(None, (h.action_id() for h in self.handlers)))