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