masklinn  committed c175935

Base implementation of extension filters

  • Participants
  • Parent commits 2ab040b
  • Branches default

Comments (0)

Files changed (2)

 Advanced topics in extends: Configuring extends behavior
-There are times when, as the original system's creator, you don't want
-the loading specification to be "everything in the process". For
-instance, you might want to filter extender classes based on a request
-attribute or a specific package/subpackage structure or position.
+By default, extends considers all the currently loaded classes for
+inclusions into the extension scheme. But whether you're the system's
+original writer or one of its user, it might not be what you want, and
+you could need to accept or reject extensions to original types based
+on entirely arbitrary criteria.
-Extends provides some tools to filter out extenders you don't need
-based on pretty arbitrary rules.
+To that end, extends provides a powerful but simple filtering API: you
+can provide a callable to any subclass of ``extends.Base`` (including
+``extends.Base`` itself), and for each extender candidate found that
+callable will be called with the class being extended and the
+potential extender. If it returns ``False`` then the candidate will be
+ignored [#]_.
+    >>> class Rejector(extends.Base):
+    ...     def accepted_extension(self):
+    ...         return False
+    >>> Rejector.extension_filter(
+    ...     lambda *args: False)
+    >>> class Rejecteee(extends.Extend, Rejector):
+    ...     def accepted_extension(self):
+    ...         return True
+    >>> Rejector().accepted_extension()
+    False
 Limitations of Extends
 class: not only would that generate some trouble performance-wise, but
 more importantly it has too high a chance of wreaking havoc on two
 different libraries using Extends in the same project.
+.. [#] In most situations in Python a falsy value (empty list,
+       ``None``, ``0``, ...) suffices, but this API specifically
+       requires ``False`` to ignore an extender.

File extends/

     return [
         sub for sub in cls.__subclasses__()
         if issubclass(sub, Extend)
-        if sub is not Extend][::-1]
+        if sub is not Extend
+        if cls._accepts(sub)][::-1]
 class Extend(object):
         extenders = (base_extenders or [cls]) + ancestor_extenders
         return object.__new__(
             type(cls.__name__, tuple(extenders), {}))
+    @classmethod
+    def extension_filter(cls, callable):
+        if not hasattr(cls, '_extension_filters'):
+            cls._extension_filters = [callable]
+        else:
+            cls._extension_filters.append(callable)
+    @classmethod
+    def _accepts(cls, candidate):
+        if not hasattr(cls, '_extension_filters'): return True
+        for callable in cls._extension_filters:
+            if callable(cls, candidate) is False:
+                return False
+        return super(cls)._accepts(candidate)