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