1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """This module provides bases for predicates dispatching (the pattern in use
19 here is similar to what's refered as multi-dispatch or predicate-dispatch in the
20 literature, though a bit different since the idea is to select across different
21 implementation 'e.g. classes), not to dispatch a message to a function or
22 method. It contains the following classes:
23
24 * :class:`RegistryStore`, the top level object which loads implementation
25 objects and stores them into registries. You'll usually use it to access
26 registries and their contained objects;
27
28 * :class:`Registry`, the base class which contains objects semantically grouped
29 (for instance, sharing a same API, hence the 'implementation' name). You'll
30 use it to select the proper implementation according to a context. Notice you
31 may use registries on their own without using the store.
32
33 .. Note::
34
35 implementation objects are usually designed to be accessed through the
36 registry and not by direct instantiation, besides to use it as base classe.
37
38 The selection procedure is delegated to a selector, which is responsible for
39 scoring the object according to some context. At the end of the selection, if an
40 implementation has been found, an instance of this class is returned. A selector
41 is built from one or more predicates combined together using AND, OR, NOT
42 operators (actually `&`, `|` and `~`). You'll thus find some base classes to
43 build predicates:
44
45 * :class:`Predicate`, the abstract base predicate class
46
47 * :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you
48 shouldn't have to use directly. You'll use `&`, `|` and '~' operators between
49 predicates directly
50
51 * :func:`objectify_predicate`
52
53 You'll eventually find one concrete predicate: :class:`yes`
54
55 .. autoclass:: RegistryStore
56 .. autoclass:: Registry
57
58 Predicates
59 ----------
60 .. autoclass:: Predicate
61 .. autofunc:: objectify_predicate
62 .. autoclass:: yes
63
64 Debugging
65 ---------
66 .. autoclass:: traced_selection
67
68 Exceptions
69 ----------
70 .. autoclass:: RegistryException
71 .. autoclass:: RegistryNotFound
72 .. autoclass:: ObjectNotFound
73 .. autoclass:: NoSelectableObject
74 """
75
76 __docformat__ = "restructuredtext en"
77
78 import sys
79 import types
80 import weakref
81 from os import listdir, stat
82 from os.path import join, isdir, exists
83 from logging import getLogger
84
85 from logilab.common.logging_ext import set_log_methods
89 """Base class for registry exception."""
90
92 """Raised when an unknown registry is requested.
93
94 This is usually a programming/typo error.
95 """
96
98 """Raised when an unregistered object is requested.
99
100 This may be a programming/typo or a misconfiguration error.
101 """
102
104 """Raised when no object is selectable for a given context."""
105 - def __init__(self, args, kwargs, objects):
106 self.args = args
107 self.kwargs = kwargs
108 self.objects = objects
109
111 return ('args: %s, kwargs: %s\ncandidates: %s'
112 % (self.args, list(self.kwargs.keys()), self.objects))
113
116 """Return a dictionary of <modname>: <modpath> and an ordered list of
117 (file, module name) to load
118 """
119 from logilab.common.modutils import modpath_from_file
120 if _toload is None:
121 assert isinstance(path, list)
122 _toload = {}, []
123 for fileordir in path:
124 if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
125 subfiles = [join(fileordir, fname) for fname in listdir(fileordir)]
126 _toload_info(subfiles, extrapath, _toload)
127 elif fileordir[-3:] == '.py':
128 modpath = modpath_from_file(fileordir, extrapath)
129
130
131
132
133
134
135
136
137
138
139
140 if modpath[-1] == '__init__':
141 modpath.pop()
142 modname = '.'.join(modpath)
143 _toload[0][modname] = fileordir
144 _toload[1].append((fileordir, modname))
145 return _toload
146
149 """returns a unique identifier for an object class"""
150 return '%s.%s' % (cls.__module__, cls.__name__)
151
153 """return a tuple of registry names (see __registries__)"""
154 if registryname:
155 return (registryname,)
156 return cls.__registries__
157
160 """The registry store a set of implementations associated to identifier:
161
162 * to each identifier are associated a list of implementations
163
164 * to select an implementation of a given identifier, you should use one of the
165 :meth:`select` or :meth:`select_or_none` method
166
167 * to select a list of implementations for a context, you should use the
168 :meth:`possible_objects` method
169
170 * dictionary like access to an identifier will return the bare list of
171 implementations for this identifier.
172
173 To be usable in a registry, the only requirement is to have a `__select__`
174 attribute.
175
176 At the end of the registration process, the :meth:`__registered__`
177 method is called on each registered object which have them, given the
178 registry in which it's registered as argument.
179
180 Registration methods:
181
182 .. automethod: register
183 .. automethod: unregister
184
185 Selection methods:
186
187 .. automethod: select
188 .. automethod: select_or_none
189 .. automethod: possible_objects
190 .. automethod: object_by_id
191 """
195
197 """return the registry (list of implementation objects) associated to
198 this name
199 """
200 try:
201 return super(Registry, self).__getitem__(name)
202 except KeyError:
203 raise ObjectNotFound(name).with_traceback(sys.exc_info()[-1])
204
206 """call method __registered__() on registered objects when the callback
207 is defined"""
208 for objects in self.values():
209 for objectcls in objects:
210 registered = getattr(objectcls, '__registered__', None)
211 if registered:
212 registered(self)
213 if self.debugmode:
214 wrap_predicates(_lltrace)
215
216 - def register(self, obj, oid=None, clear=False):
217 """base method to add an object in the registry"""
218 assert not '__abstract__' in obj.__dict__
219 assert obj.__select__
220 oid = oid or obj.__regid__
221 assert oid, ('no explicit name supplied to register object %s, '
222 'which has no __regid__ set' % obj)
223 if clear:
224 objects = self[oid] = []
225 else:
226 objects = self.setdefault(oid, [])
227 assert not obj in objects, \
228 'object %s is already registered' % obj
229 objects.append(obj)
230
232 """remove <replaced> and register <obj>"""
233
234
235
236 if not isinstance(replaced, str):
237 replaced = classid(replaced)
238
239 assert obj is not replaced, 'replacing an object by itself: %s' % obj
240 registered_objs = self.get(obj.__regid__, ())
241 for index, registered in enumerate(registered_objs):
242 if classid(registered) == replaced:
243 del registered_objs[index]
244 break
245 else:
246 self.warning('trying to replace %s that is not registered with %s',
247 replaced, obj)
248 self.register(obj)
249
251 """remove object <obj> from this registry"""
252 clsid = classid(obj)
253 oid = obj.__regid__
254 for registered in self.get(oid, ()):
255
256
257 if classid(registered) == clsid:
258 self[oid].remove(registered)
259 break
260 else:
261 self.warning('can\'t remove %s, no id %s in the registry',
262 clsid, oid)
263
265 """return a list containing all objects in this registry.
266 """
267 result = []
268 for objs in list(self.values()):
269 result += objs
270 return result
271
272
273
275 """return object with the `oid` identifier. Only one object is expected
276 to be found.
277
278 raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
279
280 raise :exc:`AssertionError` if there is more than one object there
281 """
282 objects = self[oid]
283 assert len(objects) == 1, objects
284 return objects[0](*args, **kwargs)
285
286 - def select(self, __oid, *args, **kwargs):
287 """return the most specific object among those with the given oid
288 according to the given context.
289
290 raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
291
292 raise :exc:`NoSelectableObject` if not object apply
293 """
294 obj = self._select_best(self[__oid], *args, **kwargs)
295 if obj is None:
296 raise NoSelectableObject(args, kwargs, self[__oid] )
297 return obj
298
300 """return the most specific object among those with the given oid
301 according to the given context, or None if no object applies.
302 """
303 try:
304 return self.select(__oid, *args, **kwargs)
305 except (NoSelectableObject, ObjectNotFound):
306 return None
307
309 """return an iterator on possible objects in this registry for the given
310 context
311 """
312 for objects in self.values():
313 obj = self._select_best(objects, *args, **kwargs)
314 if obj is None:
315 continue
316 yield obj
317
319 """return an instance of the most specific object according
320 to parameters
321
322 return None if not object apply (don't raise `NoSelectableObject` since
323 it's costly when searching objects using `possible_objects`
324 (e.g. searching for hooks).
325 """
326 score, winners = 0, None
327 for obj in objects:
328 objectscore = obj.__select__(obj, *args, **kwargs)
329 if objectscore > score:
330 score, winners = objectscore, [obj]
331 elif objectscore > 0 and objectscore == score:
332 winners.append(obj)
333 if winners is None:
334 return None
335 if len(winners) > 1:
336
337 msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)'
338 if self.debugmode:
339
340 raise Exception(msg % (winners, args, list(kwargs.keys())))
341 self.error(msg, winners, args, list(kwargs.keys()))
342
343 return winners[0](*args, **kwargs)
344
345
346
347 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
348
351 """This class is responsible for loading implementations and storing them
352 in their registry which are created on the fly as needed.
353
354 It handles dynamic registration of objects and provides a convenient api to
355 access them. To be recognized as an object that should be stored into one of
356 the store's registry (:class:`Registry`), an object (usually a class) has
357 the following attributes, used control how they interact with the registry:
358
359 :attr:`__registry__` or `__registries__`
360 name of the registry for this object (string like 'views', 'templates'...)
361 or list of registry names if you want your object to be added to multiple
362 registries
363
364 :attr:`__regid__`
365 implementation's identifier in the registry (string like 'main',
366 'primary', 'folder_box')
367
368 :attr:`__select__`
369 the implementation's selector
370
371 Moreover, the :attr:`__abstract__` attribute may be set to `True` to
372 indicate that a class is abstract and should not be registered (inherited
373 attributes not considered).
374
375 .. Note::
376
377 When using the store to load objects dynamically, you *always* have
378 to use **super()** to get the methods and attributes of the
379 superclasses, and not use the class identifier. Else, you'll get into
380 trouble when reloading comes into the place.
381
382 For example, instead of writing::
383
384 class Thing(Parent):
385 __regid__ = 'athing'
386 __select__ = yes()
387 def f(self, arg1):
388 Parent.f(self, arg1)
389
390 You must write::
391
392 class Thing(Parent):
393 __regid__ = 'athing'
394 __select__ = yes()
395 def f(self, arg1):
396 super(Parent, self).f(arg1)
397
398 Controlling objects registration
399 --------------------------------
400
401 Dynamic loading is triggered by calling the :meth:`register_objects` method,
402 given a list of directory to inspect for python modules.
403
404 .. automethod: register_objects
405
406 For each module, by default, all compatible objects are registered
407 automatically, though if some objects have to replace other objects, or have
408 to be included only if some condition is met, you'll have to define a
409 `registration_callback(vreg)` function in your module and explicitly
410 register **all objects** in this module, using the api defined below.
411
412
413 .. automethod:: RegistryStore.register_all
414 .. automethod:: RegistryStore.register_and_replace
415 .. automethod:: RegistryStore.register
416 .. automethod:: RegistryStore.unregister
417
418 .. Note::
419 Once the function `registration_callback(vreg)` is implemented in a
420 module, all the objects from this module have to be explicitly
421 registered as it disables the automatic objects registration.
422
423
424 Examples:
425
426 .. sourcecode:: python
427
428 # cubicweb/web/views/basecomponents.py
429 def registration_callback(store):
430 # register everything in the module except SeeAlsoComponent
431 store.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
432 # conditionally register SeeAlsoVComponent
433 if 'see_also' in store.schema:
434 store.register(SeeAlsoVComponent)
435
436 In this example, we register all application object classes defined in the module
437 except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
438 relation type is defined in the instance'schema.
439
440 .. sourcecode:: python
441
442 # goa/appobjects/sessions.py
443 def registration_callback(store):
444 store.register(SessionsCleaner)
445 # replace AuthenticationManager by GAEAuthenticationManager
446 store.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
447 # replace PersistentSessionManager by GAEPersistentSessionManager
448 store.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
449
450 In this example, we explicitly register classes one by one:
451
452 * the `SessionCleaner` class
453 * the `GAEAuthenticationManager` to replace the `AuthenticationManager`
454 * the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
455
456 If at some point we register a new appobject class in this module, it won't be
457 registered at all without modification to the `registration_callback`
458 implementation. The previous example will register it though, thanks to the call
459 to the `register_all` method.
460
461 Controlling registry instantation
462 ---------------------------------
463 The `REGISTRY_FACTORY` class dictionary allows to specify which class should
464 be instantiated for a given registry name. The class associated to `None` in
465 it will be the class used when there is no specific class for a name.
466 """
467
471
473 """clear all registries managed by this store"""
474
475 for subdict in self.values():
476 subdict.clear()
477 self._lastmodifs = {}
478
480 """return the registry (dictionary of class objects) associated to
481 this name
482 """
483 try:
484 return super(RegistryStore, self).__getitem__(name)
485 except KeyError:
486 raise RegistryNotFound(name).with_traceback(sys.exc_info()[-1])
487
488
489
490
491 REGISTRY_FACTORY = {None: Registry}
492
494 """return existing registry named regid or use factory to create one and
495 return it"""
496 try:
497 return self.REGISTRY_FACTORY[regid]
498 except KeyError:
499 return self.REGISTRY_FACTORY[None]
500
507
509 """register all `objects` given. Objects which are not from the module
510 `modname` or which are in `butclasses` won't be registered.
511
512 Typical usage is:
513
514 .. sourcecode:: python
515
516 store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))
517
518 So you get partially automatic registration, keeping manual registration
519 for some object (to use
520 :meth:`~logilab.common.registry.RegistryStore.register_and_replace`
521 for instance)
522 """
523 assert isinstance(modname, str), \
524 'modname expected to be a module name (ie string), got %r' % modname
525 for obj in objects:
526 try:
527 if obj.__module__ != modname or obj in butclasses:
528 continue
529 oid = obj.__regid__
530 except AttributeError:
531 continue
532 if oid and not obj.__dict__.get('__abstract__'):
533 self.register(obj, oid=oid)
534
535 - def register(self, obj, registryname=None, oid=None, clear=False):
536 """register `obj` implementation into `registryname` or
537 `obj.__registry__` if not specified, with identifier `oid` or
538 `obj.__regid__` if not specified.
539
540 If `clear` is true, all objects with the same identifier will be
541 previously unregistered.
542 """
543 assert not obj.__dict__.get('__abstract__')
544 try:
545 vname = obj.__name__
546 except AttributeError:
547
548 vname = obj.__class__.__name__
549 for registryname in class_registries(obj, registryname):
550 registry = self.setdefault(registryname)
551 registry.register(obj, oid=oid, clear=clear)
552 self.debug('register %s in %s[\'%s\']',
553 vname, registryname, oid or obj.__regid__)
554 self._loadedmods.setdefault(obj.__module__, {})[classid(obj)] = obj
555
557 """unregister `obj` implementation object from the registry
558 `registryname` or `obj.__registry__` if not specified.
559 """
560 for registryname in class_registries(obj, registryname):
561 self[registryname].unregister(obj)
562
564 """register `obj` implementation object into `registryname` or
565 `obj.__registry__` if not specified. If found, the `replaced` object
566 will be unregistered first (else a warning will be issued as it's
567 generally unexpected).
568 """
569 for registryname in class_registries(obj, registryname):
570 self[registryname].register_and_replace(obj, replaced)
571
572
573
575 """reset registry and walk down path to return list of (path, name)
576 file modules to be loaded"""
577
578 self.reset()
579
580 self._toloadmods, filemods = _toload_info(path, extrapath)
581
582
583
584 self._loadedmods = {}
585 return filemods
586
595
597 """call initialization_completed() on all known registries"""
598 for reg in self.values():
599 reg.initialization_completed()
600
602 try:
603 return stat(filepath)[-2]
604 except OSError:
605
606 self.warning('Unable to load %s. It is likely to be a backup file',
607 filepath)
608 return None
609
611 """return True if something module changed and the registry should be
612 reloaded
613 """
614 lastmodifs = self._lastmodifs
615 for fileordir in path:
616 if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
617 if self.is_reload_needed([join(fileordir, fname)
618 for fname in listdir(fileordir)]):
619 return True
620 elif fileordir[-3:] == '.py':
621 mdate = self._mdate(fileordir)
622 if mdate is None:
623 continue
624 elif "flymake" in fileordir:
625
626 continue
627 if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate:
628 self.info('File %s changed since last visit', fileordir)
629 return True
630 return False
631
633 """load app objects from a python file"""
634 from logilab.common.modutils import load_module_from_name
635 if modname in self._loadedmods:
636 return
637 self._loadedmods[modname] = {}
638 mdate = self._mdate(filepath)
639 if mdate is None:
640 return
641 elif "flymake" in filepath:
642
643 return
644
645
646
647 self._lastmodifs[filepath] = mdate
648
649 module = load_module_from_name(modname)
650 self.load_module(module)
651
653 """load objects from a module using registration_callback() when it exists
654 """
655 self.info('loading %s from %s', module.__name__, module.__file__)
656 if hasattr(module, 'registration_callback'):
657 module.registration_callback(self)
658 else:
659 for objname, obj in list(vars(module).items()):
660 if objname.startswith('_'):
661 continue
662 self._load_ancestors_then_object(module.__name__, obj)
663
665 """handle automatic object class registration:
666
667 - first ensure parent classes are already registered
668
669 - class with __abstract__ == True in their local dictionary or
670 with a name starting with an underscore are not registered
671
672 - object class needs to have __registry__ and __regid__ attributes
673 set to a non empty string to be registered.
674 """
675
676 objmodname = getattr(objectcls, '__module__', None)
677 if objmodname != modname:
678 if objmodname in self._toloadmods:
679 self.load_file(self._toloadmods[objmodname], objmodname)
680 return
681
682 try:
683 if not (getattr(objectcls, '__regid__', None)
684 and getattr(objectcls, '__select__', None)):
685 return
686 except TypeError:
687 return
688 clsid = classid(objectcls)
689 if clsid in self._loadedmods[modname]:
690 return
691 self._loadedmods[modname][clsid] = objectcls
692 for parent in objectcls.__bases__:
693 self._load_ancestors_then_object(modname, parent)
694 if (objectcls.__dict__.get('__abstract__')
695 or objectcls.__name__[0] == '_'
696 or not objectcls.__registries__
697 or not objectcls.__regid__):
698 return
699 try:
700 self.register(objectcls)
701 except Exception as ex:
702 if self.debugmode:
703 raise
704 self.exception('object %s registration failed: %s',
705 objectcls, ex)
706
707
708
709 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
710
711
712
713 set_log_methods(RegistryStore, getLogger('registry.store'))
714 set_log_methods(Registry, getLogger('registry'))
715
716
717
718 TRACED_OIDS = None
721 vobj = args[0]
722 if TRACED_OIDS == 'all' or vobj.__regid__ in TRACED_OIDS:
723 print('%s -> %s for %s(%s)' % (cls, ret, vobj, vobj.__regid__))
724
726 """use this decorator on your predicates so they become traceable with
727 :class:`traced_selection`
728 """
729 def traced(cls, *args, **kwargs):
730 ret = selector(cls, *args, **kwargs)
731 if TRACED_OIDS is not None:
732 _trace_selector(cls, selector, args, ret)
733 return ret
734 traced.__name__ = selector.__name__
735 traced.__doc__ = selector.__doc__
736 return traced
737
739 """
740 Typical usage is :
741
742 .. sourcecode:: python
743
744 >>> from logilab.common.registry import traced_selection
745 >>> with traced_selection():
746 ... # some code in which you want to debug selectors
747 ... # for all objects
748
749 Don't forget the 'from __future__ import with_statement' at the module top-level
750 if you're using python prior to 2.6.
751
752 This will yield lines like this in the logs::
753
754 selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
755
756 You can also give to :class:`traced_selection` the identifiers of objects on
757 which you want to debug selection ('oid1' and 'oid2' in the example above).
758
759 .. sourcecode:: python
760
761 >>> with traced_selection( ('regid1', 'regid2') ):
762 ... # some code in which you want to debug selectors
763 ... # for objects with __regid__ 'regid1' and 'regid2'
764
765 A potentially useful point to set up such a tracing function is
766 the `logilab.common.registry.Registry.select` method body.
767 """
768
771
775
776 - def __exit__(self, exctype, exc, traceback):
780
784 """Most of the time, a simple score function is enough to build a selector.
785 The :func:`objectify_predicate` decorator turn it into a proper selector
786 class::
787
788 @objectify_predicate
789 def one(cls, req, rset=None, **kwargs):
790 return 1
791
792 class MyView(View):
793 __select__ = View.__select__ & one()
794
795 """
796 return type(selector_func.__name__, (Predicate,),
797 {'__doc__': selector_func.__doc__,
798 '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
799
800
801 _PREDICATES = {}
804 for predicate in _PREDICATES.values():
805 if not '_decorators' in predicate.__dict__:
806 predicate._decorators = set()
807 if decorator in predicate._decorators:
808 continue
809 predicate._decorators.add(decorator)
810 predicate.__call__ = decorator(predicate.__call__)
811
819
820 -class Predicate(object, metaclass=PredicateMetaClass):
821 """base class for selector classes providing implementation
822 for operators ``&``, ``|`` and ``~``
823
824 This class is only here to give access to binary operators, the selector
825 logic itself should be implemented in the :meth:`__call__` method. Notice it
826 should usually accept any arbitrary arguments (the context), though that may
827 vary depending on your usage of the registry.
828
829 a selector is called to help choosing the correct object for a
830 particular context by returning a score (`int`) telling how well
831 the implementation given as first argument fit to the given context.
832
833 0 score means that the class doesn't apply.
834 """
835
836 @property
838
839 return self.__class__.__name__
840
842 """search for the given selector, selector instance or tuple of
843 selectors in the selectors tree. Return None if not found.
844 """
845 if self is selector:
846 return self
847 if (isinstance(selector, type) or isinstance(selector, tuple)) and \
848 isinstance(self, selector):
849 return self
850 return None
851
853 return self.__class__.__name__
854
867
870
871
872
873 - def __call__(self, cls, *args, **kwargs):
874 return NotImplementedError("selector %s must implement its logic "
875 "in its __call__ method" % self.__class__)
876
878 return '<Predicate %s at %x>' % (self.__class__.__name__, id(self))
879
882 """base class for compound selector classes"""
883
886
888 return '%s(%s)' % (self.__class__.__name__,
889 ','.join(str(s) for s in self.selectors))
890
891 @classmethod
893 """deal with selector instanciation when necessary and merge
894 multi-selectors if possible:
895
896 AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
897 ==> AndPredicate(sel1, sel2, sel3, sel4)
898 """
899 merged_selectors = []
900 for selector in selectors:
901
902
903 if isinstance(selector, types.FunctionType):
904 selector = objectify_predicate(selector)()
905 if isinstance(selector, type) and issubclass(selector, Predicate):
906 selector = selector()
907 assert isinstance(selector, Predicate), selector
908 if isinstance(selector, cls):
909 merged_selectors += selector.selectors
910 else:
911 merged_selectors.append(selector)
912 return merged_selectors
913
915 """search for the given selector or selector instance (or tuple of
916 selectors) in the selectors tree. Return None if not found
917 """
918 for childselector in self.selectors:
919 if childselector is selector:
920 return childselector
921 found = childselector.search_selector(selector)
922 if found is not None:
923 return found
924
925 return super(MultiPredicate, self).search_selector(selector)
926
929 """and-chained selectors"""
930 - def __call__(self, cls, *args, **kwargs):
931 score = 0
932 for selector in self.selectors:
933 partscore = selector(cls, *args, **kwargs)
934 if not partscore:
935 return 0
936 score += partscore
937 return score
938
941 """or-chained selectors"""
942 - def __call__(self, cls, *args, **kwargs):
943 for selector in self.selectors:
944 partscore = selector(cls, *args, **kwargs)
945 if partscore:
946 return partscore
947 return 0
948
950 """negation selector"""
952 self.selector = selector
953
954 - def __call__(self, cls, *args, **kwargs):
955 score = self.selector(cls, *args, **kwargs)
956 return int(not score)
957
959 return 'NOT(%s)' % self.selector
960
961
962 -class yes(Predicate):
963 """Return the score given as parameter, with a default score of 0.5 so any
964 other selector take precedence.
965
966 Usually used for objects which can be selected whatever the context, or
967 also sometimes to add arbitrary points to a score.
968
969 Take care, `yes(0)` could be named 'no'...
970 """
973
976