1
1
""" generic mechanism for marking and selecting python functions. """
2
2
import 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 )
3
9
4
10
5
11
class MarkerError (Exception ):
@@ -182,7 +188,7 @@ def __getattr__(self, name):
182
188
raise AttributeError ("Marker name must NOT start with underscore" )
183
189
if hasattr (self , '_config' ):
184
190
self ._check (name )
185
- return MarkDecorator (name )
191
+ return MarkDecorator (Mark ( name , (), {}) )
186
192
187
193
def _check (self , name ):
188
194
try :
@@ -235,19 +241,20 @@ def test_function():
235
241
additional keyword or positional arguments.
236
242
237
243
"""
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' )
242
251
243
252
@property
244
253
def markname (self ):
245
254
return self .name # for backward-compat (2.4.1 had this attr)
246
255
247
256
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
251
258
252
259
def __call__ (self , * args , ** kwargs ):
253
260
""" if passed a single callable argument: decorate it with mark info.
@@ -270,17 +277,14 @@ def __call__(self, *args, **kwargs):
270
277
else :
271
278
holder = getattr (func , self .name , None )
272
279
if holder is None :
273
- holder = MarkInfo (
274
- self .name , self .args , self .kwargs
275
- )
280
+ holder = MarkInfo (self .mark )
276
281
setattr (func , self .name , holder )
277
282
else :
278
- holder .add (self .args , self . kwargs )
283
+ holder .add_mark (self .mark )
279
284
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 ))
284
288
285
289
286
290
def extract_argvalue (maybe_marked_args ):
@@ -291,36 +295,41 @@ def extract_argvalue(maybe_marked_args):
291
295
newmarks = {}
292
296
argval = maybe_marked_args
293
297
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
297
301
argval = argval .args [- 1 ]
298
302
return argval , newmarks
299
303
300
304
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 ):
302
315
""" 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' )
311
324
312
325
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 )
316
327
317
- def add (self , args , kwargs ):
328
+ def add_mark (self , mark ):
318
329
""" 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 )
322
332
323
333
def __iter__ (self ):
324
334
""" 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