summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Peuch <cortex@worlddomination.be>2019-12-19 05:25:57 +0100
committerLaurent Peuch <cortex@worlddomination.be>2019-12-19 05:25:57 +0100
commit832c6b5aa425e1a3191affcb7185342ca21518cb (patch)
treebbec35e1953a308f7abcd3ba633b6bbea24919c3
parent3df3f3f0ddc7d444a66f4d1e17f8c04a125bbe93 (diff)
downloadlogilab-common-832c6b5aa425e1a3191affcb7185342ca21518cb.tar.gz
[mod] move predicates code up in the file for the next patch
This is needed for type declarations because we need to reference the Predicate class.
-rw-r--r--logilab/common/registry.py417
1 files changed, 208 insertions, 209 deletions
diff --git a/logilab/common/registry.py b/logilab/common/registry.py
index 3310864..86d5219 100644
--- a/logilab/common/registry.py
+++ b/logilab/common/registry.py
@@ -97,6 +97,214 @@ from logilab.common.decorators import classproperty
from logilab.common.deprecation import deprecated
+# selector base classes and operations ########################################
+
+def objectify_predicate(selector_func):
+ """Most of the time, a simple score function is enough to build a selector.
+ The :func:`objectify_predicate` decorator turn it into a proper selector
+ class::
+
+ @objectify_predicate
+ def one(cls, req, rset=None, **kwargs):
+ return 1
+
+ class MyView(View):
+ __select__ = View.__select__ & one()
+
+ """
+ return type(selector_func.__name__, (Predicate,),
+ {'__doc__': selector_func.__doc__,
+ '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
+
+
+_PREDICATES: Dict[int, Type] = {}
+
+def wrap_predicates(decorator):
+ for predicate in _PREDICATES.values():
+ if not '_decorators' in predicate.__dict__:
+ predicate._decorators = set()
+ if decorator in predicate._decorators:
+ continue
+ predicate._decorators.add(decorator)
+ predicate.__call__ = decorator(predicate.__call__)
+
+class PredicateMetaClass(type):
+ def __new__(mcs, *args, **kwargs):
+ # use __new__ so subclasses doesn't have to call Predicate.__init__
+ inst = type.__new__(mcs, *args, **kwargs)
+ proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p)))
+ _PREDICATES[id(proxy)] = proxy
+ return inst
+
+
+class Predicate(object, metaclass=PredicateMetaClass):
+ """base class for selector classes providing implementation
+ for operators ``&``, ``|`` and ``~``
+
+ This class is only here to give access to binary operators, the selector
+ logic itself should be implemented in the :meth:`__call__` method. Notice it
+ should usually accept any arbitrary arguments (the context), though that may
+ vary depending on your usage of the registry.
+
+ a selector is called to help choosing the correct object for a
+ particular context by returning a score (`int`) telling how well
+ the implementation given as first argument fit to the given context.
+
+ 0 score means that the class doesn't apply.
+ """
+
+ @property
+ def func_name(self):
+ # backward compatibility
+ return self.__class__.__name__
+
+ def search_selector(self, selector):
+ """search for the given selector, selector instance or tuple of
+ selectors in the selectors tree. Return None if not found.
+ """
+ if self is selector:
+ return self
+ if (isinstance(selector, type) or isinstance(selector, tuple)) and \
+ isinstance(self, selector):
+ return self
+ return None
+
+ def __str__(self):
+ return self.__class__.__name__
+
+ def __and__(self, other):
+ return AndPredicate(self, other)
+ def __rand__(self, other):
+ return AndPredicate(other, self)
+ def __iand__(self, other):
+ return AndPredicate(self, other)
+ def __or__(self, other):
+ return OrPredicate(self, other)
+ def __ror__(self, other):
+ return OrPredicate(other, self)
+ def __ior__(self, other):
+ return OrPredicate(self, other)
+
+ def __invert__(self):
+ return NotPredicate(self)
+
+ # XXX (function | function) or (function & function) not managed yet
+
+ def __call__(self, cls, *args, **kwargs):
+ return NotImplementedError("selector %s must implement its logic "
+ "in its __call__ method" % self.__class__)
+
+ def __repr__(self):
+ return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
+
+
+class MultiPredicate(Predicate):
+ """base class for compound selector classes"""
+
+ def __init__(self, *selectors):
+ self.selectors = self.merge_selectors(selectors)
+
+ def __str__(self):
+ return '%s(%s)' % (self.__class__.__name__,
+ ','.join(str(s) for s in self.selectors))
+
+ @classmethod
+ def merge_selectors(cls, selectors):
+ """deal with selector instanciation when necessary and merge
+ multi-selectors if possible:
+
+ AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
+ ==> AndPredicate(sel1, sel2, sel3, sel4)
+ """
+ merged_selectors = []
+ for selector in selectors:
+ # XXX do we really want magic-transformations below?
+ # if so, wanna warn about them?
+ if isinstance(selector, types.FunctionType):
+ selector = objectify_predicate(selector)()
+ if isinstance(selector, type) and issubclass(selector, Predicate):
+ selector = selector()
+ assert isinstance(selector, Predicate), selector
+ if isinstance(selector, cls):
+ merged_selectors += selector.selectors
+ else:
+ merged_selectors.append(selector)
+ return merged_selectors
+
+ def search_selector(self, selector):
+ """search for the given selector or selector instance (or tuple of
+ selectors) in the selectors tree. Return None if not found
+ """
+ for childselector in self.selectors:
+ if childselector is selector:
+ return childselector
+ found = childselector.search_selector(selector)
+ if found is not None:
+ return found
+ # if not found in children, maybe we are looking for self?
+ return super(MultiPredicate, self).search_selector(selector)
+
+
+class AndPredicate(MultiPredicate):
+ """and-chained selectors"""
+ def __call__(self, cls, *args, **kwargs):
+ score = 0
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if not partscore:
+ return 0
+ score += partscore
+ return score
+
+
+class OrPredicate(MultiPredicate):
+ """or-chained selectors"""
+ def __call__(self, cls, *args, **kwargs):
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if partscore:
+ return partscore
+ return 0
+
+class NotPredicate(Predicate):
+ """negation selector"""
+ def __init__(self, selector):
+ self.selector = selector
+
+ def __call__(self, cls, *args, **kwargs):
+ score = self.selector(cls, *args, **kwargs)
+ return int(not score)
+
+ def __str__(self):
+ return 'NOT(%s)' % self.selector
+
+
+class yes(Predicate): # pylint: disable=C0103
+ """Return the score given as parameter, with a default score of 0.5 so any
+ other selector take precedence.
+
+ Usually used for objects which can be selected whatever the context, or
+ also sometimes to add arbitrary points to a score.
+
+ Take care, `yes(0)` could be named 'no'...
+ """
+ def __init__(self, score=0.5):
+ self.score = score
+
+ def __call__(self, *args, **kwargs):
+ return self.score
+
+
+# deprecated stuff #############################################################
+
+@deprecated('[lgc 0.59] use Registry.objid class method instead')
+def classid(cls):
+ return '%s.%s' % (cls.__module__, cls.__name__)
+
+@deprecated('[lgc 0.59] use obj_registries function instead')
+def class_registries(cls, registryname):
+ return obj_registries(cls, registryname)
+
class RegistryException(Exception):
"""Base class for registry exception."""
@@ -990,212 +1198,3 @@ class traced_selection(object): # pylint: disable=C0103
global TRACED_OIDS
TRACED_OIDS = None
return traceback is None
-
-# selector base classes and operations ########################################
-
-def objectify_predicate(selector_func):
- """Most of the time, a simple score function is enough to build a selector.
- The :func:`objectify_predicate` decorator turn it into a proper selector
- class::
-
- @objectify_predicate
- def one(cls, req, rset=None, **kwargs):
- return 1
-
- class MyView(View):
- __select__ = View.__select__ & one()
-
- """
- return type(selector_func.__name__, (Predicate,),
- {'__doc__': selector_func.__doc__,
- '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
-
-
-_PREDICATES: Dict[int, Type] = {}
-
-def wrap_predicates(decorator):
- for predicate in _PREDICATES.values():
- if not '_decorators' in predicate.__dict__:
- predicate._decorators = set()
- if decorator in predicate._decorators:
- continue
- predicate._decorators.add(decorator)
- predicate.__call__ = decorator(predicate.__call__)
-
-class PredicateMetaClass(type):
- def __new__(mcs, *args, **kwargs):
- # use __new__ so subclasses doesn't have to call Predicate.__init__
- inst = type.__new__(mcs, *args, **kwargs)
- proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p)))
- _PREDICATES[id(proxy)] = proxy
- return inst
-
-
-class Predicate(object, metaclass=PredicateMetaClass):
- """base class for selector classes providing implementation
- for operators ``&``, ``|`` and ``~``
-
- This class is only here to give access to binary operators, the selector
- logic itself should be implemented in the :meth:`__call__` method. Notice it
- should usually accept any arbitrary arguments (the context), though that may
- vary depending on your usage of the registry.
-
- a selector is called to help choosing the correct object for a
- particular context by returning a score (`int`) telling how well
- the implementation given as first argument fit to the given context.
-
- 0 score means that the class doesn't apply.
- """
-
- @property
- def func_name(self):
- # backward compatibility
- return self.__class__.__name__
-
- def search_selector(self, selector):
- """search for the given selector, selector instance or tuple of
- selectors in the selectors tree. Return None if not found.
- """
- if self is selector:
- return self
- if (isinstance(selector, type) or isinstance(selector, tuple)) and \
- isinstance(self, selector):
- return self
- return None
-
- def __str__(self):
- return self.__class__.__name__
-
- def __and__(self, other):
- return AndPredicate(self, other)
- def __rand__(self, other):
- return AndPredicate(other, self)
- def __iand__(self, other):
- return AndPredicate(self, other)
- def __or__(self, other):
- return OrPredicate(self, other)
- def __ror__(self, other):
- return OrPredicate(other, self)
- def __ior__(self, other):
- return OrPredicate(self, other)
-
- def __invert__(self):
- return NotPredicate(self)
-
- # XXX (function | function) or (function & function) not managed yet
-
- def __call__(self, cls, *args, **kwargs):
- return NotImplementedError("selector %s must implement its logic "
- "in its __call__ method" % self.__class__)
-
- def __repr__(self):
- return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
-
-
-class MultiPredicate(Predicate):
- """base class for compound selector classes"""
-
- def __init__(self, *selectors):
- self.selectors = self.merge_selectors(selectors)
-
- def __str__(self):
- return '%s(%s)' % (self.__class__.__name__,
- ','.join(str(s) for s in self.selectors))
-
- @classmethod
- def merge_selectors(cls, selectors):
- """deal with selector instanciation when necessary and merge
- multi-selectors if possible:
-
- AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
- ==> AndPredicate(sel1, sel2, sel3, sel4)
- """
- merged_selectors = []
- for selector in selectors:
- # XXX do we really want magic-transformations below?
- # if so, wanna warn about them?
- if isinstance(selector, types.FunctionType):
- selector = objectify_predicate(selector)()
- if isinstance(selector, type) and issubclass(selector, Predicate):
- selector = selector()
- assert isinstance(selector, Predicate), selector
- if isinstance(selector, cls):
- merged_selectors += selector.selectors
- else:
- merged_selectors.append(selector)
- return merged_selectors
-
- def search_selector(self, selector):
- """search for the given selector or selector instance (or tuple of
- selectors) in the selectors tree. Return None if not found
- """
- for childselector in self.selectors:
- if childselector is selector:
- return childselector
- found = childselector.search_selector(selector)
- if found is not None:
- return found
- # if not found in children, maybe we are looking for self?
- return super(MultiPredicate, self).search_selector(selector)
-
-
-class AndPredicate(MultiPredicate):
- """and-chained selectors"""
- def __call__(self, cls, *args, **kwargs):
- score = 0
- for selector in self.selectors:
- partscore = selector(cls, *args, **kwargs)
- if not partscore:
- return 0
- score += partscore
- return score
-
-
-class OrPredicate(MultiPredicate):
- """or-chained selectors"""
- def __call__(self, cls, *args, **kwargs):
- for selector in self.selectors:
- partscore = selector(cls, *args, **kwargs)
- if partscore:
- return partscore
- return 0
-
-class NotPredicate(Predicate):
- """negation selector"""
- def __init__(self, selector):
- self.selector = selector
-
- def __call__(self, cls, *args, **kwargs):
- score = self.selector(cls, *args, **kwargs)
- return int(not score)
-
- def __str__(self):
- return 'NOT(%s)' % self.selector
-
-
-class yes(Predicate): # pylint: disable=C0103
- """Return the score given as parameter, with a default score of 0.5 so any
- other selector take precedence.
-
- Usually used for objects which can be selected whatever the context, or
- also sometimes to add arbitrary points to a score.
-
- Take care, `yes(0)` could be named 'no'...
- """
- def __init__(self, score=0.5):
- self.score = score
-
- def __call__(self, *args, **kwargs):
- return self.score
-
-
-# deprecated stuff #############################################################
-
-@deprecated('[lgc 0.59] use Registry.objid class method instead')
-def classid(cls):
- return '%s.%s' % (cls.__module__, cls.__name__)
-
-@deprecated('[lgc 0.59] use obj_registries function instead')
-def class_registries(cls, registryname):
- return obj_registries(cls, registryname)
-