appobject.py
changeset 5564 d13830a2adfd
parent 5426 0d4853a6e5ee
child 5901 782b27eaf97a
equal deleted inserted replaced
5563:26e242ba3da5 5564:d13830a2adfd
    37 
    37 
    38 from logilab.common.deprecation import deprecated
    38 from logilab.common.deprecation import deprecated
    39 from logilab.common.decorators import classproperty
    39 from logilab.common.decorators import classproperty
    40 from logilab.common.logging_ext import set_log_methods
    40 from logilab.common.logging_ext import set_log_methods
    41 
    41 
       
    42 from cubicweb.cwconfig import CubicWebConfiguration
       
    43 
       
    44 def class_regid(cls):
       
    45     """returns a unique identifier for an appobject class"""
       
    46     if 'id' in cls.__dict__:
       
    47         warn('[3.6] %s.%s: id is deprecated, use __regid__'
       
    48              % (cls.__module__, cls.__name__), DeprecationWarning)
       
    49         cls.__regid__ = cls.id
       
    50     if hasattr(cls, 'id') and not isinstance(cls.id, property):
       
    51         return cls.id
       
    52     return cls.__regid__
       
    53 
       
    54 # helpers for debugging selectors
       
    55 TRACED_OIDS = None
       
    56 
       
    57 def _trace_selector(cls, selector, args, ret):
       
    58     # /!\ lltrace decorates pure function or __call__ method, this
       
    59     #     means argument order may be different
       
    60     if isinstance(cls, Selector):
       
    61         selname = str(cls)
       
    62         vobj = args[0]
       
    63     else:
       
    64         selname = selector.__name__
       
    65         vobj = cls
       
    66     if TRACED_OIDS == 'all' or class_regid(vobj) in TRACED_OIDS:
       
    67         #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
       
    68         print '%s -> %s for %s(%s)' % (selname, ret, vobj, vobj.__regid__)
       
    69 
       
    70 def lltrace(selector):
       
    71     """use this decorator on your selectors so the becomes traceable with
       
    72     :class:`traced_selection`
       
    73     """
       
    74     # don't wrap selectors if not in development mode
       
    75     if CubicWebConfiguration.mode == 'system': # XXX config.debug
       
    76         return selector
       
    77     def traced(cls, *args, **kwargs):
       
    78         ret = selector(cls, *args, **kwargs)
       
    79         if TRACED_OIDS is not None:
       
    80             _trace_selector(cls, selector, args, ret)
       
    81         return ret
       
    82     traced.__name__ = selector.__name__
       
    83     traced.__doc__ = selector.__doc__
       
    84     return traced
       
    85 
       
    86 class traced_selection(object):
       
    87     """
       
    88     Typical usage is :
       
    89 
       
    90     .. sourcecode:: python
       
    91 
       
    92         >>> from cubicweb.selectors import traced_selection
       
    93         >>> with traced_selection():
       
    94         ...     # some code in which you want to debug selectors
       
    95         ...     # for all objects
       
    96 
       
    97     Don't forget the 'from __future__ import with_statement' at the module top-level
       
    98     if you're using python prior to 2.6.
       
    99 
       
   100     This will yield lines like this in the logs::
       
   101 
       
   102         selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
       
   103 
       
   104     You can also give to :class:`traced_selection` the identifiers of objects on
       
   105     which you want to debug selection ('oid1' and 'oid2' in the example above).
       
   106 
       
   107     .. sourcecode:: python
       
   108 
       
   109         >>> with traced_selection( ('regid1', 'regid2') ):
       
   110         ...     # some code in which you want to debug selectors
       
   111         ...     # for objects with __regid__ 'regid1' and 'regid2'
       
   112 
       
   113     A potentially usefull point to set up such a tracing function is
       
   114     the `cubicweb.vregistry.Registry.select` method body.
       
   115     """
       
   116 
       
   117     def __init__(self, traced='all'):
       
   118         self.traced = traced
       
   119 
       
   120     def __enter__(self):
       
   121         global TRACED_OIDS
       
   122         TRACED_OIDS = self.traced
       
   123 
       
   124     def __exit__(self, exctype, exc, traceback):
       
   125         global TRACED_OIDS
       
   126         TRACED_OIDS = None
       
   127         return traceback is None
    42 
   128 
    43 # selector base classes and operations ########################################
   129 # selector base classes and operations ########################################
    44 
   130 
    45 def objectify_selector(selector_func):
   131 def objectify_selector(selector_func):
    46     """Most of the time, a simple score function is enough to build a selector.
   132     """Most of the time, a simple score function is enough to build a selector.
   173         return None
   259         return None
   174 
   260 
   175 
   261 
   176 class AndSelector(MultiSelector):
   262 class AndSelector(MultiSelector):
   177     """and-chained selectors (formerly known as chainall)"""
   263     """and-chained selectors (formerly known as chainall)"""
       
   264     @lltrace
   178     def __call__(self, cls, *args, **kwargs):
   265     def __call__(self, cls, *args, **kwargs):
   179         score = 0
   266         score = 0
   180         for selector in self.selectors:
   267         for selector in self.selectors:
   181             partscore = selector(cls, *args, **kwargs)
   268             partscore = selector(cls, *args, **kwargs)
   182             if not partscore:
   269             if not partscore:
   185         return score
   272         return score
   186 
   273 
   187 
   274 
   188 class OrSelector(MultiSelector):
   275 class OrSelector(MultiSelector):
   189     """or-chained selectors (formerly known as chainfirst)"""
   276     """or-chained selectors (formerly known as chainfirst)"""
       
   277     @lltrace
   190     def __call__(self, cls, *args, **kwargs):
   278     def __call__(self, cls, *args, **kwargs):
   191         for selector in self.selectors:
   279         for selector in self.selectors:
   192             partscore = selector(cls, *args, **kwargs)
   280             partscore = selector(cls, *args, **kwargs)
   193             if partscore:
   281             if partscore:
   194                 return partscore
   282                 return partscore
   197 class NotSelector(Selector):
   285 class NotSelector(Selector):
   198     """negation selector"""
   286     """negation selector"""
   199     def __init__(self, selector):
   287     def __init__(self, selector):
   200         self.selector = selector
   288         self.selector = selector
   201 
   289 
       
   290     @lltrace
   202     def __call__(self, cls, *args, **kwargs):
   291     def __call__(self, cls, *args, **kwargs):
   203         score = self.selector(cls, *args, **kwargs)
   292         score = self.selector(cls, *args, **kwargs)
   204         return int(not score)
   293         return int(not score)
   205 
   294 
   206     def __str__(self):
   295     def __str__(self):