11""" generic mechanism for marking and selecting python functions. """
22import inspect
3+ from collections import namedtuple
4+ from operator import attrgetter
5+ from .compat import imap
6+
7+ def alias (name ):
8+ return property (attrgetter (name ), doc = 'alias for ' + name )
39
410
511class MarkerError (Exception ):
@@ -182,7 +188,7 @@ def __getattr__(self, name):
182188 raise AttributeError ("Marker name must NOT start with underscore" )
183189 if hasattr (self , '_config' ):
184190 self ._check (name )
185- return MarkDecorator (name )
191+ return MarkDecorator (Mark ( name , (), {}) )
186192
187193 def _check (self , name ):
188194 try :
@@ -235,19 +241,20 @@ def test_function():
235241 additional keyword or positional arguments.
236242
237243 """
238- def __init__ (self , name , args = None , kwargs = None ):
239- self .name = name
240- self .args = args or ()
241- self .kwargs = kwargs or {}
244+ def __init__ (self , mark ):
245+ assert isinstance (mark , Mark ), repr (mark )
246+ self .mark = mark
247+
248+ name = alias ('mark.name' )
249+ args = alias ('mark.args' )
250+ kwargs = alias ('mark.kwargs' )
242251
243252 @property
244253 def markname (self ):
245254 return self .name # for backward-compat (2.4.1 had this attr)
246255
247256 def __repr__ (self ):
248- d = self .__dict__ .copy ()
249- name = d .pop ('name' )
250- return "<MarkDecorator %r %r>" % (name , d )
257+ return "<MarkDecorator %r>" % self .mark
251258
252259 def __call__ (self , * args , ** kwargs ):
253260 """ if passed a single callable argument: decorate it with mark info.
@@ -270,17 +277,14 @@ def __call__(self, *args, **kwargs):
270277 else :
271278 holder = getattr (func , self .name , None )
272279 if holder is None :
273- holder = MarkInfo (
274- self .name , self .args , self .kwargs
275- )
280+ holder = MarkInfo (self .mark )
276281 setattr (func , self .name , holder )
277282 else :
278- holder .add (self .args , self . kwargs )
283+ holder .add_mark (self .mark )
279284 return func
280- kw = self .kwargs .copy ()
281- kw .update (kwargs )
282- args = self .args + args
283- return self .__class__ (self .name , args = args , kwargs = kw )
285+
286+ mark = Mark (self .name , args , kwargs )
287+ return self .__class__ (self .mark .combined_with (mark ))
284288
285289
286290def extract_argvalue (maybe_marked_args ):
@@ -291,36 +295,41 @@ def extract_argvalue(maybe_marked_args):
291295 newmarks = {}
292296 argval = maybe_marked_args
293297 while isinstance (argval , MarkDecorator ):
294- newmark = MarkDecorator (argval . markname ,
295- argval .args [:- 1 ], argval .kwargs )
296- newmarks [newmark .markname ] = newmark
298+ newmark = MarkDecorator (Mark (
299+ argval . markname , argval .args [:- 1 ], argval .kwargs ) )
300+ newmarks [newmark .name ] = newmark
297301 argval = argval .args [- 1 ]
298302 return argval , newmarks
299303
300304
301- class MarkInfo :
305+ class Mark (namedtuple ('Mark' , 'name, args, kwargs' )):
306+
307+ def combined_with (self , other ):
308+ assert self .name == other .name
309+ return Mark (
310+ self .name , self .args + other .args ,
311+ dict (self .kwargs , ** other .kwargs ))
312+
313+
314+ class MarkInfo (object ):
302315 """ Marking object created by :class:`MarkDecorator` instances. """
303- def __init__ (self , name , args , kwargs ):
304- #: name of attribute
305- self .name = name
306- #: positional argument list, empty if none specified
307- self . args = args
308- #: keyword argument dictionary, empty if nothing specified
309- self . kwargs = kwargs . copy ( )
310- self . _arglist = [( args , kwargs . copy ())]
316+ def __init__ (self , mark ):
317+ assert isinstance ( mark , Mark ), repr ( mark )
318+ self .combined = mark
319+ self . _marks = [ mark ]
320+
321+ name = alias ( 'combined.name' )
322+ args = alias ( 'combined.args' )
323+ kwargs = alias ( 'combined.kwargs' )
311324
312325 def __repr__ (self ):
313- return "<MarkInfo %r args=%r kwargs=%r>" % (
314- self .name , self .args , self .kwargs
315- )
326+ return "<MarkInfo {0!r}>" .format (self .combined )
316327
317- def add (self , args , kwargs ):
328+ def add_mark (self , mark ):
318329 """ add a MarkInfo with the given args and kwargs. """
319- self ._arglist .append ((args , kwargs ))
320- self .args += args
321- self .kwargs .update (kwargs )
330+ self ._marks .append (mark )
331+ self .combined = self .combined .combined_with (mark )
322332
323333 def __iter__ (self ):
324334 """ yield MarkInfo objects each relating to a marking-call. """
325- for args , kwargs in self ._arglist :
326- yield MarkInfo (self .name , args , kwargs )
335+ return imap (MarkInfo , self ._marks )
0 commit comments