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 from logilab.common.modutils import load_module_from_name
724 if modname in self._loadedmods:
725 return
726 self._loadedmods[modname] = {}
727 mdate = self._mdate(filepath)
728 if mdate is None:
729 return
730 elif "flymake" in filepath:
731
732 return
733
734
735
736 self._lastmodifs[filepath] = mdate
737
738 module = load_module_from_name(modname)
739 self.load_module(module)
740
742 """Automatically handle module objects registration.
743
744 Instances are registered as soon as they are hashable and have the
745 following attributes:
746
747 * __regid__ (a string)
748 * __select__ (a callable)
749 * __registries__ (a tuple/list of string)
750
751 For classes this is a bit more complicated :
752
753 - first ensure parent classes are already registered
754
755 - class with __abstract__ == True in their local dictionary are skipped
756
757 - object class needs to have registries and identifier properly set to a
758 non empty string to be registered.
759 """
760 self.info('loading %s from %s', module.__name__, module.__file__)
761 if hasattr(module, 'registration_callback'):
762 module.registration_callback(self)
763 else:
764 self.register_all(vars(module).values(), module.__name__)
765
767 """handle class registration according to rules defined in
768 :meth:`load_module`
769 """
770
771 if not isinstance(objectcls, type):
772 if self.is_registrable(objectcls) and objectcls.__module__ == modname:
773 self.register(objectcls)
774 return
775
776 objmodname = objectcls.__module__
777 if objmodname != modname:
778
779
780
781 if objmodname in self._toloadmods:
782
783
784 self.load_file(self._toloadmods[objmodname], objmodname)
785 return
786
787 clsid = '%s.%s' % (modname, objectcls.__name__)
788 if clsid in self._loadedmods[modname]:
789 return
790 self._loadedmods[modname][clsid] = objectcls
791
792 for parent in objectcls.__bases__:
793 self._load_ancestors_then_object(modname, parent, butclasses)
794
795 if objectcls in butclasses or not self.is_registrable(objectcls):
796 return
797
798 reg = self.setdefault(obj_registries(objectcls)[0])
799 if reg.objname(objectcls)[0] == '_':
800 warn("[lgc 0.59] object whose name start with '_' won't be "
801 "skipped anymore at some point, use __abstract__ = True "
802 "instead (%s)" % objectcls, DeprecationWarning)
803 return
804
805 self.register(objectcls)
806
807 @classmethod
809 """ensure `obj` should be registered
810
811 as arbitrary stuff may be registered, do a lot of check and warn about
812 weird cases (think to dumb proxy objects)
813 """
814 if isinstance(obj, type):
815 if not issubclass(obj, RegistrableObject):
816
817 if not (getattr(obj, '__registries__', None)
818 and getattr(obj, '__regid__', None)
819 and getattr(obj, '__select__', None)):
820 return False
821 elif issubclass(obj, RegistrableInstance):
822 return False
823 elif not isinstance(obj, RegistrableInstance):
824 return False
825 if not obj.__regid__:
826 return False
827 registries = obj.__registries__
828 if not registries:
829 return False
830 selector = obj.__select__
831 if not selector:
832 return False
833 if obj.__dict__.get('__abstract__', False):
834 return False
835
836 if not isinstance(registries, (tuple, list)):
837 cls.warning('%s has __registries__ which is not a list or tuple', obj)
838 return False
839 if not callable(selector):
840 cls.warning('%s has not callable __select__', obj)
841 return False
842 return True
843
844
845
846 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
847
848
849
850 set_log_methods(RegistryStore, getLogger('registry.store'))
851 set_log_methods(Registry, getLogger('registry'))
852
853
854
855 TRACED_OIDS = None
861
863 """use this decorator on your predicates so they become traceable with
864 :class:`traced_selection`
865 """
866 def traced(cls, *args, **kwargs):
867 ret = selector(cls, *args, **kwargs)
868 if TRACED_OIDS is not None:
869 _trace_selector(cls, selector, args, ret)
870 return ret
871 traced.__name__ = selector.__name__
872 traced.__doc__ = selector.__doc__
873 return traced
874
876 """
877 Typical usage is :
878
879 .. sourcecode:: python
880
881 >>> from logilab.common.registry import traced_selection
882 >>> with traced_selection():
883 ... # some code in which you want to debug selectors
884 ... # for all objects
885
886 This will yield lines like this in the logs::
887
888 selector one_line_rset returned 0 for <class 'elephant.Babar'>
889
890 You can also give to :class:`traced_selection` the identifiers of objects on
891 which you want to debug selection ('oid1' and 'oid2' in the example above).
892
893 .. sourcecode:: python
894
895 >>> with traced_selection( ('regid1', 'regid2') ):
896 ... # some code in which you want to debug selectors
897 ... # for objects with __regid__ 'regid1' and 'regid2'
898
899 A potentially useful point to set up such a tracing function is
900 the `logilab.common.registry.Registry.select` method body.
901 """
902
905
909
910 - def __exit__(self, exctype, exc, traceback):
914
918 """Most of the time, a simple score function is enough to build a selector.
919 The :func:`objectify_predicate` decorator turn it into a proper selector
920 class::
921
922 @objectify_predicate
923 def one(cls, req, rset=None, **kwargs):
924 return 1
925
926 class MyView(View):
927 __select__ = View.__select__ & one()
928
929 """
930 return type(selector_func.__name__, (Predicate,),
931 {'__doc__': selector_func.__doc__,
932 '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
933
934
935 _PREDICATES = {}
938 for predicate in _PREDICATES.values():
939 if not '_decorators' in predicate.__dict__:
940 predicate._decorators = set()
941 if decorator in predicate._decorators:
942 continue
943 predicate._decorators.add(decorator)
944 predicate.__call__ = decorator(predicate.__call__)
945
953
957 """base class for selector classes providing implementation
958 for operators ``&``, ``|`` and ``~``
959
960 This class is only here to give access to binary operators, the selector
961 logic itself should be implemented in the :meth:`__call__` method. Notice it
962 should usually accept any arbitrary arguments (the context), though that may
963 vary depending on your usage of the registry.
964
965 a selector is called to help choosing the correct object for a
966 particular context by returning a score (`int`) telling how well
967 the implementation given as first argument fit to the given context.
968
969 0 score means that the class doesn't apply.
970 """
971
972 @property
974
975 return self.__class__.__name__
976
978 """search for the given selector, selector instance or tuple of
979 selectors in the selectors tree. Return None if not found.
980 """
981 if self is selector:
982 return self
983 if (isinstance(selector, type) or isinstance(selector, tuple)) and \
984 isinstance(self, selector):
985 return self
986 return None
987
989 return self.__class__.__name__
990
1003
1006
1007
1008
1009 - def __call__(self, cls, *args, **kwargs):
1010 return NotImplementedError("selector %s must implement its logic "
1011 "in its __call__ method" % self.__class__)
1012
1014 return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
1015
1018 """base class for compound selector classes"""
1019
1022
1024 return '%s(%s)' % (self.__class__.__name__,
1025 ','.join(str(s) for s in self.selectors))
1026
1027 @classmethod
1029 """deal with selector instanciation when necessary and merge
1030 multi-selectors if possible:
1031
1032 AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
1033 ==> AndPredicate(sel1, sel2, sel3, sel4)
1034 """
1035 merged_selectors = []
1036 for selector in selectors:
1037
1038
1039 if isinstance(selector, types.FunctionType):
1040 selector = objectify_predicate(selector)()
1041 if isinstance(selector, type) and issubclass(selector, Predicate):
1042 selector = selector()
1043 assert isinstance(selector, Predicate), selector
1044 if isinstance(selector, cls):
1045 merged_selectors += selector.selectors
1046 else:
1047 merged_selectors.append(selector)
1048 return merged_selectors
1049
1051 """search for the given selector or selector instance (or tuple of
1052 selectors) in the selectors tree. Return None if not found
1053 """
1054 for childselector in self.selectors:
1055 if childselector is selector:
1056 return childselector
1057 found = childselector.search_selector(selector)
1058 if found is not None:
1059 return found
1060
1061 return super(MultiPredicate, self).search_selector(selector)
1062
1065 """and-chained selectors"""
1066 - def __call__(self, cls, *args, **kwargs):
1067 score = 0
1068 for selector in self.selectors:
1069 partscore = selector(cls, *args, **kwargs)
1070 if not partscore:
1071 return 0
1072 score += partscore
1073 return score
1074
1077 """or-chained selectors"""
1078 - def __call__(self, cls, *args, **kwargs):
1079 for selector in self.selectors:
1080 partscore = selector(cls, *args, **kwargs)
1081 if partscore:
1082 return partscore
1083 return 0
1084
1086 """negation selector"""
1088 self.selector = selector
1089
1090 - def __call__(self, cls, *args, **kwargs):
1091 score = self.selector(cls, *args, **kwargs)
1092 return int(not score)
1093
1095 return 'NOT(%s)' % self.selector
1096
1097
1098 -class yes(Predicate):
1099 """Return the score given as parameter, with a default score of 0.5 so any
1100 other selector take precedence.
1101
1102 Usually used for objects which can be selected whatever the context, or
1103 also sometimes to add arbitrary points to a score.
1104
1105 Take care, `yes(0)` could be named 'no'...
1106 """
1109
1112
1113
1114
1115
1116 from logilab.common.deprecation import deprecated
1117
1118 @deprecated('[lgc 0.59] use Registry.objid class method instead')
1119 -def classid(cls):
1120 return '%s.%s' % (cls.__module__, cls.__name__)
1121
1122 @deprecated('[lgc 0.59] use obj_registries function instead')
1123 -def class_registries(cls, registryname):
1125