""" generic mechanism for marking and selecting python functions. """importpytest,pydefpytest_namespace():return{'mark':MarkGenerator()}defpytest_addoption(parser):group=parser.getgroup("general")group._addoption('-k',action="store",dest="keyword",default='',metavar="KEYWORDEXPR",help="only run tests which match given keyword expression. ""An expression consists of space-separated terms. ""Each term must match. Precede a term with '-' to negate. ""Terminate expression with ':' to make the first match match ""all subsequent tests (usually file-order). ")group._addoption("-m",action="store",dest="markexpr",default="",metavar="MARKEXPR",help="only run tests matching given mark expression. ""example: -m 'mark1 and not mark2'.")group.addoption("--markers",action="store_true",help="show markers (builtin, plugin and per-project ones).")parser.addini("markers","markers for test functions",'linelist')defpytest_cmdline_main(config):ifconfig.option.markers:config.pluginmanager.do_configure(config)tw=py.io.TerminalWriter()forlineinconfig.getini("markers"):name,rest=line.split(":",1)tw.write("@pytest.mark.%s:"%name,bold=True)tw.line(rest)tw.line()config.pluginmanager.do_unconfigure(config)return0pytest_cmdline_main.tryfirst=Truedefpytest_collection_modifyitems(items,config):keywordexpr=config.option.keywordmatchexpr=config.option.markexprifnotkeywordexprandnotmatchexpr:returnselectuntil=Falseifkeywordexpr[-1:]==":":selectuntil=Truekeywordexpr=keywordexpr[:-1]remaining=[]deselected=[]forcoliteminitems:ifkeywordexprandskipbykeyword(colitem,keywordexpr):deselected.append(colitem)else:ifselectuntil:keywordexpr=Noneifmatchexpr:ifnotmatchmark(colitem,matchexpr):deselected.append(colitem)continueremaining.append(colitem)ifdeselected:config.hook.pytest_deselected(items=deselected)items[:]=remainingclassBoolDict:def__init__(self,mydict):self._mydict=mydictdef__getitem__(self,name):returnnameinself._mydictdefmatchmark(colitem,matchexpr):returneval(matchexpr,{},BoolDict(colitem.obj.__dict__))defpytest_configure(config):ifconfig.option.strict:pytest.mark._config=configdefskipbykeyword(colitem,keywordexpr):""" return True if they given keyword expression means to skip this collector/item. """ifnotkeywordexpr:returnitemkeywords=getkeywords(colitem)forkeyinfilter(None,keywordexpr.split()):eor=key[:1]=='-'ifeor:key=key[1:]ifnot(eor^matchonekeyword(key,itemkeywords)):returnTruedefgetkeywords(node):keywords={}whilenodeisnotNone:keywords.update(node.keywords)node=node.parentreturnkeywordsdefmatchonekeyword(key,itemkeywords):foreleminkey.split("."):forkwinitemkeywords:ifeleminkw:breakelse:returnFalsereturnTrueclassMarkGenerator:""" Factory for :class:`MarkDecorator` objects - exposed as a ``py.test.mark`` singleton instance. Example:: import py @py.test.mark.slowtest def test_function(): pass will set a 'slowtest' :class:`MarkInfo` object on the ``test_function`` object. """def__getattr__(self,name):ifname[0]=="_":raiseAttributeError(name)ifhasattr(self,'_config'):self._check(name)returnMarkDecorator(name)def_check(self,name):try:ifnameinself._markers:returnexceptAttributeError:passself._markers=l=set()forlineinself._config.getini("markers"):beginning=line.split(":",1)x=beginning[0].split("(",1)[0]l.add(x)ifnamenotinself._markers:raiseAttributeError("%r not a registered marker"%(name,))classMarkDecorator:""" A decorator for test functions and test classes. When applied it will create :class:`MarkInfo` objects which may be :ref:`retrieved by hooks as item keywords <excontrolskip>`. MarkDecorator instances are often created like this:: mark1 = py.test.mark.NAME # simple MarkDecorator mark2 = py.test.mark.NAME(name1=value) # parametrized MarkDecorator and can then be applied as decorators to test functions:: @mark2 def test_function(): pass """def__init__(self,name,args=None,kwargs=None):self.markname=nameself.args=argsor()self.kwargs=kwargsor{}def__repr__(self):d=self.__dict__.copy()name=d.pop('markname')return"<MarkDecorator %r%r>"%(name,d)def__call__(self,*args,**kwargs):""" if passed a single callable argument: decorate it with mark info. otherwise add *args/**kwargs in-place to mark information. """ifargs:func=args[0]iflen(args)==1andhasattr(func,'__call__')or \
hasattr(func,'__bases__'):ifhasattr(func,'__bases__'):ifhasattr(func,'pytestmark'):l=func.pytestmarkifnotisinstance(l,list):func.pytestmark=[l,self]else:l.append(self)else:func.pytestmark=[self]else:holder=getattr(func,self.markname,None)ifholderisNone:holder=MarkInfo(self.markname,self.args,self.kwargs)setattr(func,self.markname,holder)else:holder.add(self.args,self.kwargs)returnfunckw=self.kwargs.copy()kw.update(kwargs)args=self.args+argsreturnself.__class__(self.markname,args=args,kwargs=kw)classMarkInfo:""" Marking object created by :class:`MarkDecorator` instances. """def__init__(self,name,args,kwargs):#: name of attributeself.name=name#: positional argument list, empty if none specifiedself.args=args#: keyword argument dictionary, empty if nothing specifiedself.kwargs=kwargsself._arglist=[(args,kwargs.copy())]def__repr__(self):return"<MarkInfo %r args=%r kwargs=%r>"%(self.name,self.args,self.kwargs)defadd(self,args,kwargs):""" add a MarkInfo with the given args and kwargs. """self._arglist.append((args,kwargs))self.args+=argsself.kwargs.update(kwargs)def__iter__(self):""" yield MarkInfo objects each relating to a marking-call. """forargs,kwargsinself._arglist:yieldMarkInfo(self.name,args,kwargs)