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 from __future__ import print_function
77
78 __docformat__ = "restructuredtext en"
79
80 import sys
81 import types
82 import weakref
83 import traceback as tb
84 from os import listdir, stat
85 from os.path import join, isdir, exists
86 from logging import getLogger
87 from warnings import warn
88
89 from six import string_types, add_metaclass
90
91 from logilab.common.modutils import modpath_from_file
92 from logilab.common.logging_ext import set_log_methods
93 from logilab.common.decorators import classproperty
97 """Base class for registry exception."""
98
100 """Raised when an unknown registry is requested.
101
102 This is usually a programming/typo error.
103 """
104
106 """Raised when an unregistered object is requested.
107
108 This may be a programming/typo or a misconfiguration error.
109 """
110
112 """Raised when no object is selectable for a given context."""
113 - def __init__(self, args, kwargs, objects):
114 self.args = args
115 self.kwargs = kwargs
116 self.objects = objects
117
119 return ('args: %s, kwargs: %s\ncandidates: %s'
120 % (self.args, self.kwargs.keys(), self.objects))
121
123 """Raised when several objects compete at selection time with an equal
124 score.
125
126 """
127
130 modpath = modpath_from_file(path, extrapath)
131
132
133
134
135
136
137
138
139
140
141
142 if modpath[-1] == '__init__':
143 modpath.pop()
144 return '.'.join(modpath)
145
148 """Return a dictionary of <modname>: <modpath> and an ordered list of
149 (file, module name) to load
150 """
151 if _toload is None:
152 assert isinstance(path, list)
153 _toload = {}, []
154 for fileordir in path:
155 if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
156 subfiles = [join(fileordir, fname) for fname in listdir(fileordir)]
157 _toload_info(subfiles, extrapath, _toload)
158 elif fileordir[-3:] == '.py':
159 modname = _modname_from_path(fileordir, extrapath)
160 _toload[0][modname] = fileordir
161 _toload[1].append((fileordir, modname))
162 return _toload
163
166 """This is the base class for registrable objects which are selected
167 according to a context.
168
169 :attr:`__registry__`
170 name of the registry for this object (string like 'views',
171 'templates'...). You may want to define `__registries__` directly if your
172 object should be registered in several registries.
173
174 :attr:`__regid__`
175 object's identifier in the registry (string like 'main',
176 'primary', 'folder_box')
177
178 :attr:`__select__`
179 class'selector
180
181 Moreover, the `__abstract__` attribute may be set to True to indicate that a
182 class is abstract and should not be registered.
183
184 You don't have to inherit from this class to put it in a registry (having
185 `__regid__` and `__select__` is enough), though this is needed for classes
186 that should be automatically registered.
187 """
188
189 __registry__ = None
190 __regid__ = None
191 __select__ = None
192 __abstract__ = True
193
194 @classproperty
199
202 """Inherit this class if you want instances of the classes to be
203 automatically registered.
204 """
205
206 - def __new__(cls, *args, **kwargs):
207 """Add a __module__ attribute telling the module where the instance was
208 created, for automatic registration.
209 """
210 obj = super(RegistrableInstance, cls).__new__(cls)
211
212 filepath = tb.extract_stack(limit=2)[0][0]
213 obj.__module__ = _modname_from_path(filepath)
214 return obj
215
218 """The registry store a set of implementations associated to identifier:
219
220 * to each identifier are associated a list of implementations
221
222 * to select an implementation of a given identifier, you should use one of the
223 :meth:`select` or :meth:`select_or_none` method
224
225 * to select a list of implementations for a context, you should use the
226 :meth:`possible_objects` method
227
228 * dictionary like access to an identifier will return the bare list of
229 implementations for this identifier.
230
231 To be usable in a registry, the only requirement is to have a `__select__`
232 attribute.
233
234 At the end of the registration process, the :meth:`__registered__`
235 method is called on each registered object which have them, given the
236 registry in which it's registered as argument.
237
238 Registration methods:
239
240 .. automethod: register
241 .. automethod: unregister
242
243 Selection methods:
244
245 .. automethod: select
246 .. automethod: select_or_none
247 .. automethod: possible_objects
248 .. automethod: object_by_id
249 """
253
255 """return the registry (list of implementation objects) associated to
256 this name
257 """
258 try:
259 return super(Registry, self).__getitem__(name)
260 except KeyError:
261 exc = ObjectNotFound(name)
262 exc.__traceback__ = sys.exc_info()[-1]
263 raise exc
264
265 @classmethod
267 """returns a unique identifier for an object stored in the registry"""
268 return '%s.%s' % (obj.__module__, cls.objname(obj))
269
270 @classmethod
272 """returns a readable name for an object stored in the registry"""
273 return getattr(obj, '__name__', id(obj))
274
276 """call method __registered__() on registered objects when the callback
277 is defined"""
278 for objects in self.values():
279 for objectcls in objects:
280 registered = getattr(objectcls, '__registered__', None)
281 if registered:
282 registered(self)
283 if self.debugmode:
284 wrap_predicates(_lltrace)
285
286 - def register(self, obj, oid=None, clear=False):
287 """base method to add an object in the registry"""
288 assert not '__abstract__' in obj.__dict__, obj
289 assert obj.__select__, obj
290 oid = oid or obj.__regid__
291 assert oid, ('no explicit name supplied to register object %s, '
292 'which has no __regid__ set' % obj)
293 if clear:
294 objects = self[oid] = []
295 else:
296 objects = self.setdefault(oid, [])
297 assert not obj in objects, 'object %s is already registered' % obj
298 objects.append(obj)
299
301 """remove <replaced> and register <obj>"""
302
303
304
305 if not isinstance(replaced, string_types):
306 replaced = self.objid(replaced)
307
308 assert obj is not replaced, 'replacing an object by itself: %s' % obj
309 registered_objs = self.get(obj.__regid__, ())
310 for index, registered in enumerate(registered_objs):
311 if self.objid(registered) == replaced:
312 del registered_objs[index]
313 break
314 else:
315 self.warning('trying to replace %s that is not registered with %s',
316 replaced, obj)
317 self.register(obj)
318
320 """remove object <obj> from this registry"""
321 objid = self.objid(obj)
322 oid = obj.__regid__
323 for registered in self.get(oid, ()):
324
325
326 if self.objid(registered) == objid:
327 self[oid].remove(registered)
328 break
329 else:
330 self.warning('can\'t remove %s, no id %s in the registry',
331 objid, oid)
332
334 """return a list containing all objects in this registry.
335 """
336 result = []
337 for objs in self.values():
338 result += objs
339 return result
340
341
342
344 """return object with the `oid` identifier. Only one object is expected
345 to be found.
346
347 raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
348 registry
349
350 raise :exc:`AssertionError` if there is more than one object there
351 """
352 objects = self[oid]
353 assert len(objects) == 1, objects
354 return objects[0](*args, **kwargs)
355
356 - def select(self, __oid, *args, **kwargs):
357 """return the most specific object among those with the given oid
358 according to the given context.
359
360 raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
361 registry
362
363 raise :exc:`NoSelectableObject` if no object can be selected
364 """
365 obj = self._select_best(self[__oid], *args, **kwargs)
366 if obj is None:
367 raise NoSelectableObject(args, kwargs, self[__oid] )
368 return obj
369
371 """return the most specific object among those with the given oid
372 according to the given context, or None if no object applies.
373 """
374 try:
375 return self._select_best(self[__oid], *args, **kwargs)
376 except ObjectNotFound:
377 return None
378
380 """return an iterator on possible objects in this registry for the given
381 context
382 """
383 for objects in self.values():
384 obj = self._select_best(objects, *args, **kwargs)
385 if obj is None:
386 continue
387 yield obj
388
390 """return an instance of the most specific object according
391 to parameters
392
393 return None if not object apply (don't raise `NoSelectableObject` since
394 it's costly when searching objects using `possible_objects`
395 (e.g. searching for hooks).
396 """
397 score, winners = 0, None
398 for obj in objects:
399 objectscore = obj.__select__(obj, *args, **kwargs)
400 if objectscore > score:
401 score, winners = objectscore, [obj]
402 elif objectscore > 0 and objectscore == score:
403 winners.append(obj)
404 if winners is None:
405 return None
406 if len(winners) > 1:
407
408 msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)'
409 if self.debugmode:
410
411 raise SelectAmbiguity(msg % (winners, args, kwargs.keys()))
412 self.error(msg, winners, args, kwargs.keys())
413
414 return self.selected(winners[0], args, kwargs)
415
416 - def selected(self, winner, args, kwargs):
417 """override here if for instance you don't want "instanciation"
418 """
419 return winner(*args, **kwargs)
420
421
422
423 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
424
427 """return a tuple of registry names (see __registries__)"""
428 if registryname:
429 return (registryname,)
430 return cls.__registries__
431
434 """This class is responsible for loading objects and storing them
435 in their registry which is created on the fly as needed.
436
437 It handles dynamic registration of objects and provides a
438 convenient api to access them. To be recognized as an object that
439 should be stored into one of the store's registry
440 (:class:`Registry`), an object must provide the following
441 attributes, used control how they interact with the registry:
442
443 :attr:`__registries__`
444 list of registry names (string like 'views', 'templates'...) into which
445 the object should be registered
446
447 :attr:`__regid__`
448 object identifier in the registry (string like 'main',
449 'primary', 'folder_box')
450
451 :attr:`__select__`
452 the object predicate selectors
453
454 Moreover, the :attr:`__abstract__` attribute may be set to `True`
455 to indicate that an object is abstract and should not be registered
456 (such inherited attributes not considered).
457
458 .. Note::
459
460 When using the store to load objects dynamically, you *always* have
461 to use **super()** to get the methods and attributes of the
462 superclasses, and not use the class identifier. If not, you'll get into
463 trouble at reload time.
464
465 For example, instead of writing::
466
467 class Thing(Parent):
468 __regid__ = 'athing'
469 __select__ = yes()
470
471 def f(self, arg1):
472 Parent.f(self, arg1)
473
474 You must write::
475
476 class Thing(Parent):
477 __regid__ = 'athing'
478 __select__ = yes()
479
480 def f(self, arg1):
481 super(Thing, self).f(arg1)
482
483 Controlling object registration
484 -------------------------------
485
486 Dynamic loading is triggered by calling the
487 :meth:`register_objects` method, given a list of directories to
488 inspect for python modules.
489
490 .. automethod: register_objects
491
492 For each module, by default, all compatible objects are registered
493 automatically. However if some objects come as replacement of
494 other objects, or have to be included only if some condition is
495 met, you'll have to define a `registration_callback(vreg)`
496 function in the module and explicitly register **all objects** in
497 this module, using the api defined below.
498
499
500 .. automethod:: RegistryStore.register_all
501 .. automethod:: RegistryStore.register_and_replace
502 .. automethod:: RegistryStore.register
503 .. automethod:: RegistryStore.unregister
504
505 .. Note::
506 Once the function `registration_callback(vreg)` is implemented in a
507 module, all the objects from this module have to be explicitly
508 registered as it disables the automatic object registration.
509
510
511 Examples:
512
513 .. sourcecode:: python
514
515 def registration_callback(store):
516 # register everything in the module except BabarClass
517 store.register_all(globals().values(), __name__, (BabarClass,))
518
519 # conditionally register BabarClass
520 if 'babar_relation' in store.schema:
521 store.register(BabarClass)
522
523 In this example, we register all application object classes defined in the module
524 except `BabarClass`. This class is then registered only if the 'babar_relation'
525 relation type is defined in the instance schema.
526
527 .. sourcecode:: python
528
529 def registration_callback(store):
530 store.register(Elephant)
531 # replace Babar by Celeste
532 store.register_and_replace(Celeste, Babar)
533
534 In this example, we explicitly register classes one by one:
535
536 * the `Elephant` class
537 * the `Celeste` to replace `Babar`
538
539 If at some point we register a new appobject class in this module, it won't be
540 registered at all without modification to the `registration_callback`
541 implementation. The first example will register it though, thanks to the call
542 to the `register_all` method.
543
544 Controlling registry instantiation
545 ----------------------------------
546
547 The `REGISTRY_FACTORY` class dictionary allows to specify which class should
548 be instantiated for a given registry name. The class associated to `None`
549 key will be the class used when there is no specific class for a name.
550 """
551
555
557 """clear all registries managed by this store"""
558
559 for subdict in self.values():
560 subdict.clear()
561 self._lastmodifs = {}
562
564 """return the registry (dictionary of class objects) associated to
565 this name
566 """
567 try:
568 return super(RegistryStore, self).__getitem__(name)
569 except KeyError:
570 exc = RegistryNotFound(name)
571 exc.__traceback__ = sys.exc_info()[-1]
572 raise exc
573
574
575
576
577 REGISTRY_FACTORY = {None: Registry}
578
580 """return existing registry named regid or use factory to create one and
581 return it"""
582 try:
583 return self.REGISTRY_FACTORY[regid]
584 except KeyError:
585 return self.REGISTRY_FACTORY[None]
586
593
595 """register registrable objects into `objects`.
596
597 Registrable objects are properly configured subclasses of
598 :class:`RegistrableObject`. Objects which are not defined in the module
599 `modname` or which are in `butclasses` won't be registered.
600
601 Typical usage is:
602
603 .. sourcecode:: python
604
605 store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))
606
607 So you get partially automatic registration, keeping manual registration
608 for some object (to use
609 :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for
610 instance).
611 """
612 assert isinstance(modname, string_types), \
613 'modname expected to be a module name (ie string), got %r' % modname
614 for obj in objects:
615 if self.is_registrable(obj) and obj.__module__ == modname and not obj in butclasses:
616 if isinstance(obj, type):
617 self._load_ancestors_then_object(modname, obj, butclasses)
618 else:
619 self.register(obj)
620
621 - def register(self, obj, registryname=None, oid=None, clear=False):
622 """register `obj` implementation into `registryname` or
623 `obj.__registries__` if not specified, with identifier `oid` or
624 `obj.__regid__` if not specified.
625
626 If `clear` is true, all objects with the same identifier will be
627 previously unregistered.
628 """
629 assert not obj.__dict__.get('__abstract__'), obj
630 for registryname in obj_registries(obj, registryname):
631 registry = self.setdefault(registryname)
632 registry.register(obj, oid=oid, clear=clear)
633 self.debug("register %s in %s['%s']",
634 registry.objname(obj), registryname, oid or obj.__regid__)
635 self._loadedmods.setdefault(obj.__module__, {})[registry.objid(obj)] = obj
636
646
648 """register `obj` object into `registryname` or
649 `obj.__registries__` if not specified. If found, the `replaced` object
650 will be unregistered first (else a warning will be issued as it is
651 generally unexpected).
652 """
653 for registryname in obj_registries(obj, registryname):
654 registry = self[registryname]
655 registry.register_and_replace(obj, replaced)
656 self.debug("register %s in %s['%s'] instead of %s",
657 registry.objname(obj), registryname, obj.__regid__,
658 registry.objname(replaced))
659
660
661
663 """reset registry and walk down path to return list of (path, name)
664 file modules to be loaded"""
665
666 self.reset()
667
668 self._toloadmods, filemods = _toload_info(path, extrapath)
669
670
671
672 self._loadedmods = {}
673 return filemods
674
683
685 """call initialization_completed() on all known registries"""
686 for reg in self.values():
687 reg.initialization_completed()
688
690 """ return the modification date of a file path """
691 try:
692 return stat(filepath)[-2]
693 except OSError:
694
695 self.warning('Unable to load %s. It is likely to be a backup file',
696 filepath)
697 return None
698
700 """return True if something module changed and the registry should be
701 reloaded
702 """
703 lastmodifs = self._lastmodifs
704 for fileordir in path:
705 if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
706 if self.is_reload_needed([join(fileordir, fname)
707 for fname in listdir(fileordir)]):
708 return True
709 elif fileordir[-3:] == '.py':
710 mdate = self._mdate(fileordir)
711 if mdate is None:
712 continue
713 elif "flymake" in fileordir:
714
715 continue
716 if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate:
717 self.info('File %s changed since last visit', fileordir)
718 return True
719 return False
720
722 """ load registrable objects (if any) from a python file """
723 if modname in self._loadedmods:
724 return
725 self._loadedmods[modname] = {}
726 mdate = self._mdate(filepath)
727 if mdate is None:
728 return
729 elif "flymake" in filepath:
730
731 return
732
733
734
735 self._lastmodifs[filepath] = mdate
736
737 module = __import__(modname, fromlist=modname.split('.')[:-1])
738 self.load_module(module)
739
741 """Automatically handle module objects registration.
742
743 Instances are registered as soon as they are hashable and have the
744 following attributes:
745
746 * __regid__ (a string)
747 * __select__ (a callable)
748 * __registries__ (a tuple/list of string)
749
750 For classes this is a bit more complicated :
751
752 - first ensure parent classes are already registered
753
754 - class with __abstract__ == True in their local dictionary are skipped
755
756 - object class needs to have registries and identifier properly set to a
757 non empty string to be registered.
758 """
759 self.info('loading %s from %s', module.__name__, module.__file__)
760 if hasattr(module, 'registration_callback'):
761 module.registration_callback(self)
762 else:
763 self.register_all(vars(module).values(), module.__name__)
764
766 """handle class registration according to rules defined in
767 :meth:`load_module`
768 """
769
770 if not isinstance(objectcls, type):
771 if self.is_registrable(objectcls) and objectcls.__module__ == modname:
772 self.register(objectcls)
773 return
774
775 objmodname = objectcls.__module__
776 if objmodname != modname:
777
778
779
780 if objmodname in self._toloadmods:
781
782
783 self.load_file(self._toloadmods[objmodname], objmodname)
784 return
785
786 clsid = '%s.%s' % (modname, objectcls.__name__)
787 if clsid in self._loadedmods[modname]:
788 return
789 self._loadedmods[modname][clsid] = objectcls
790
791 for parent in objectcls.__bases__:
792 self._load_ancestors_then_object(modname, parent, butclasses)
793
794 if objectcls in butclasses or not self.is_registrable(objectcls):
795 return
796
797 reg = self.setdefault(obj_registries(objectcls)[0])
798 if reg.objname(objectcls)[0] == '_':
799 warn("[lgc 0.59] object whose name start with '_' won't be "
800 "skipped anymore at some point, use __abstract__ = True "
801 "instead (%s)" % objectcls, DeprecationWarning)
802 return
803
804 self.register(objectcls)
805
806 @classmethod
808 """ensure `obj` should be registered
809
810 as arbitrary stuff may be registered, do a lot of check and warn about
811 weird cases (think to dumb proxy objects)
812 """
813 if isinstance(obj, type):
814 if not issubclass(obj, RegistrableObject):
815
816 if not (getattr(obj, '__registries__', None)
817 and getattr(obj, '__regid__', None)
818 and getattr(obj, '__select__', None)):
819 return False
820 elif issubclass(obj, RegistrableInstance):
821 return False
822 elif not isinstance(obj, RegistrableInstance):
823 return False
824 if not obj.__regid__:
825 return False
826 registries = obj.__registries__
827 if not registries:
828 return False
829 selector = obj.__select__
830 if not selector:
831 return False
832 if obj.__dict__.get('__abstract__', False):
833 return False
834
835 if not isinstance(registries, (tuple, list)):
836 cls.warning('%s has __registries__ which is not a list or tuple', obj)
837 return False
838 if not callable(selector):
839 cls.warning('%s has not callable __select__', obj)
840 return False
841 return True
842
843
844
845 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
846
847
848
849 set_log_methods(RegistryStore, getLogger('registry.store'))
850 set_log_methods(Registry, getLogger('registry'))
851
852
853
854 TRACED_OIDS = None
860
862 """use this decorator on your predicates so they become traceable with
863 :class:`traced_selection`
864 """
865 def traced(cls, *args, **kwargs):
866 ret = selector(cls, *args, **kwargs)
867 if TRACED_OIDS is not None:
868 _trace_selector(cls, selector, args, ret)
869 return ret
870 traced.__name__ = selector.__name__
871 traced.__doc__ = selector.__doc__
872 return traced
873
875 """
876 Typical usage is :
877
878 .. sourcecode:: python
879
880 >>> from logilab.common.registry import traced_selection
881 >>> with traced_selection():
882 ... # some code in which you want to debug selectors
883 ... # for all objects
884
885 This will yield lines like this in the logs::
886
887 selector one_line_rset returned 0 for <class 'elephant.Babar'>
888
889 You can also give to :class:`traced_selection` the identifiers of objects on
890 which you want to debug selection ('oid1' and 'oid2' in the example above).
891
892 .. sourcecode:: python
893
894 >>> with traced_selection( ('regid1', 'regid2') ):
895 ... # some code in which you want to debug selectors
896 ... # for objects with __regid__ 'regid1' and 'regid2'
897
898 A potentially useful point to set up such a tracing function is
899 the `logilab.common.registry.Registry.select` method body.
900 """
901
904
908
909 - def __exit__(self, exctype, exc, traceback):
913
917 """Most of the time, a simple score function is enough to build a selector.
918 The :func:`objectify_predicate` decorator turn it into a proper selector
919 class::
920
921 @objectify_predicate
922 def one(cls, req, rset=None, **kwargs):
923 return 1
924
925 class MyView(View):
926 __select__ = View.__select__ & one()
927
928 """
929 return type(selector_func.__name__, (Predicate,),
930 {'__doc__': selector_func.__doc__,
931 '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
932
933
934 _PREDICATES = {}
937 for predicate in _PREDICATES.values():
938 if not '_decorators' in predicate.__dict__:
939 predicate._decorators = set()
940 if decorator in predicate._decorators:
941 continue
942 predicate._decorators.add(decorator)
943 predicate.__call__ = decorator(predicate.__call__)
944
952
956 """base class for selector classes providing implementation
957 for operators ``&``, ``|`` and ``~``
958
959 This class is only here to give access to binary operators, the selector
960 logic itself should be implemented in the :meth:`__call__` method. Notice it
961 should usually accept any arbitrary arguments (the context), though that may
962 vary depending on your usage of the registry.
963
964 a selector is called to help choosing the correct object for a
965 particular context by returning a score (`int`) telling how well
966 the implementation given as first argument fit to the given context.
967
968 0 score means that the class doesn't apply.
969 """
970
971 @property
973
974 return self.__class__.__name__
975
977 """search for the given selector, selector instance or tuple of
978 selectors in the selectors tree. Return None if not found.
979 """
980 if self is selector:
981 return self
982 if (isinstance(selector, type) or isinstance(selector, tuple)) and \
983 isinstance(self, selector):
984 return self
985 return None
986
988 return self.__class__.__name__
989
1002
1005
1006
1007
1008 - def __call__(self, cls, *args, **kwargs):
1009 return NotImplementedError("selector %s must implement its logic "
1010 "in its __call__ method" % self.__class__)
1011
1013 return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
1014
1017 """base class for compound selector classes"""
1018
1021
1023 return '%s(%s)' % (self.__class__.__name__,
1024 ','.join(str(s) for s in self.selectors))
1025
1026 @classmethod
1028 """deal with selector instanciation when necessary and merge
1029 multi-selectors if possible:
1030
1031 AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
1032 ==> AndPredicate(sel1, sel2, sel3, sel4)
1033 """
1034 merged_selectors = []
1035 for selector in selectors:
1036
1037
1038 if isinstance(selector, types.FunctionType):
1039 selector = objectify_predicate(selector)()
1040 if isinstance(selector, type) and issubclass(selector, Predicate):
1041 selector = selector()
1042 assert isinstance(selector, Predicate), selector
1043 if isinstance(selector, cls):
1044 merged_selectors += selector.selectors
1045 else:
1046 merged_selectors.append(selector)
1047 return merged_selectors
1048
1050 """search for the given selector or selector instance (or tuple of
1051 selectors) in the selectors tree. Return None if not found
1052 """
1053 for childselector in self.selectors:
1054 if childselector is selector:
1055 return childselector
1056 found = childselector.search_selector(selector)
1057 if found is not None:
1058 return found
1059
1060 return super(MultiPredicate, self).search_selector(selector)
1061
1064 """and-chained selectors"""
1065 - def __call__(self, cls, *args, **kwargs):
1066 score = 0
1067 for selector in self.selectors:
1068 partscore = selector(cls, *args, **kwargs)
1069 if not partscore:
1070 return 0
1071 score += partscore
1072 return score
1073
1076 """or-chained selectors"""
1077 - def __call__(self, cls, *args, **kwargs):
1078 for selector in self.selectors:
1079 partscore = selector(cls, *args, **kwargs)
1080 if partscore:
1081 return partscore
1082 return 0
1083
1085 """negation selector"""
1087 self.selector = selector
1088
1089 - def __call__(self, cls, *args, **kwargs):
1090 score = self.selector(cls, *args, **kwargs)
1091 return int(not score)
1092
1094 return 'NOT(%s)' % self.selector
1095
1096
1097 -class yes(Predicate):
1098 """Return the score given as parameter, with a default score of 0.5 so any
1099 other selector take precedence.
1100
1101 Usually used for objects which can be selected whatever the context, or
1102 also sometimes to add arbitrary points to a score.
1103
1104 Take care, `yes(0)` could be named 'no'...
1105 """
1108
1111
1112
1113
1114
1115 from logilab.common.deprecation import deprecated
1116
1117 @deprecated('[lgc 0.59] use Registry.objid class method instead')
1118 -def classid(cls):
1119 return '%s.%s' % (cls.__module__, cls.__name__)
1120
1121 @deprecated('[lgc 0.59] use obj_registries function instead')
1122 -def class_registries(cls, registryname):
1124