""" Python test discovery, setup and run of test functions. """importpyimportinspectimportsysimportpytestfrompy._code.codeimportTerminalReprfrom_pytest.monkeypatchimportmonkeypatchimport_pytestcutdir=py.path.local(_pytest.__file__).dirpath()defpytest_addoption(parser):group=parser.getgroup("general")group.addoption('--funcargs',action="store_true",dest="showfuncargs",default=False,help="show available function arguments, sorted by plugin")parser.addini("python_files",type="args",default=('test_*.py','*_test.py'),help="glob-style file patterns for Python test module discovery")parser.addini("python_classes",type="args",default=("Test",),help="prefixes for Python test class discovery")parser.addini("python_functions",type="args",default=("test",),help="prefixes for Python test function and method discovery")defpytest_cmdline_main(config):ifconfig.option.showfuncargs:showfuncargs(config)return0defpytest_generate_tests(metafunc):try:param=metafunc.function.parametrizeexceptAttributeError:returnforpinparam:metafunc.parametrize(*p.args,**p.kwargs)defpytest_configure(config):config.addinivalue_line("markers","parametrize(argnames, argvalues): call a test function multiple ""times passing in multiple different argument value sets. Example: ""@parametrize('arg1', [1,2]) would lead to two calls of the decorated ""test function, one with arg1=1 and another with arg1=2.")@pytest.mark.trylastdefpytest_namespace():raises.Exception=pytest.fail.Exceptionreturn{'raises':raises,'collect':{'Module':Module,'Class':Class,'Instance':Instance,'Function':Function,'Generator':Generator,'_fillfuncargs':fillfuncargs}}defpytest_funcarg__pytestconfig(request):""" the pytest config object with access to command line opts."""returnrequest.configdefpytest_pyfunc_call(__multicall__,pyfuncitem):ifnot__multicall__.execute():testfunction=pyfuncitem.objifpyfuncitem._isyieldedfunction():testfunction(*pyfuncitem._args)else:funcargs=pyfuncitem.funcargstestfunction(**funcargs)defpytest_collect_file(path,parent):ext=path.extpb=path.purebasenameifext==".py":ifnotparent.session.isinitpath(path):forpatinparent.config.getini('python_files'):ifpath.fnmatch(pat):breakelse:returnreturnparent.ihook.pytest_pycollect_makemodule(path=path,parent=parent)defpytest_pycollect_makemodule(path,parent):returnModule(path,parent)defpytest_pycollect_makeitem(__multicall__,collector,name,obj):res=__multicall__.execute()ifresisnotNone:returnresifinspect.isclass(obj):#if hasattr(collector.obj, 'unittest'):# return # we assume it's a mixin class for a TestCase derived oneifcollector.classnamefilter(name):ifnothasinit(obj):Class=collector._getcustomclass("Class")returnClass(name,parent=collector)elifcollector.funcnamefilter(name)andhasattr(obj,'__call__'):ifis_generator(obj):returnGenerator(name,parent=collector)else:returncollector._genfunctions(name,obj)defis_generator(func):try:returnpy.code.getrawcode(func).co_flags&32# generator functionexceptAttributeError:# builtin functions have no bytecode# assume them to not be generatorsreturnFalseclassPyobjMixin(object):defobj():deffget(self):try:returnself._objexceptAttributeError:self._obj=obj=self._getobj()returnobjdeffset(self,value):self._obj=valuereturnproperty(fget,fset,None,"underlying python object")obj=obj()def_getobj(self):returngetattr(self.parent.obj,self.name)defgetmodpath(self,stopatmodule=True,includemodule=False):""" return python path relative to the containing module. """chain=self.listchain()chain.reverse()parts=[]fornodeinchain:ifisinstance(node,Instance):continuename=node.nameifisinstance(node,Module):assertname.endswith(".py")name=name[:-3]ifstopatmodule:ifincludemodule:parts.append(name)breakparts.append(name)parts.reverse()s=".".join(parts)returns.replace(".[","[")def_getfslineno(self):try:returnself._fslinenoexceptAttributeError:passobj=self.obj# xxx let decorators etc specify a sane orderingifhasattr(obj,'place_as'):obj=obj.place_asself._fslineno=py.code.getfslineno(obj)assertisinstance(self._fslineno[1],int),objreturnself._fslinenodefreportinfo(self):# XXX caching?obj=self.objifhasattr(obj,'compat_co_firstlineno'):# nose compatibilityfspath=sys.modules[obj.__module__].__file__iffspath.endswith(".pyc"):fspath=fspath[:-1]#assert 0#fn = inspect.getsourcefile(obj) or inspect.getfile(obj)lineno=obj.compat_co_firstlinenomodpath=obj.__module__else:fspath,lineno=self._getfslineno()modpath=self.getmodpath()assertisinstance(lineno,int)returnfspath,lineno,modpathclassPyCollectorMixin(PyobjMixin,pytest.Collector):deffuncnamefilter(self,name):forprefixinself.config.getini("python_functions"):ifname.startswith(prefix):returnTruedefclassnamefilter(self,name):forprefixinself.config.getini("python_classes"):ifname.startswith(prefix):returnTruedefcollect(self):# NB. we avoid random getattrs and peek in the __dict__ instead# (XXX originally introduced from a PyPy need, still true?)dicts=[getattr(self.obj,'__dict__',{})]forbaseclsininspect.getmro(self.obj.__class__):dicts.append(basecls.__dict__)seen={}l=[]fordicindicts:forname,objindic.items():ifnameinseen:continueseen[name]=Trueifname[0]!="_":res=self.makeitem(name,obj)ifresisNone:continueifnotisinstance(res,list):res=[res]l.extend(res)l.sort(key=lambdaitem:item.reportinfo()[:2])returnldefmakeitem(self,name,obj):returnself.ihook.pytest_pycollect_makeitem(collector=self,name=name,obj=obj)def_genfunctions(self,name,funcobj):module=self.getparent(Module).objclscol=self.getparent(Class)cls=clscolandclscol.objorNonetransfer_markers(funcobj,cls,module)metafunc=Metafunc(funcobj,config=self.config,cls=cls,module=module)gentesthook=self.config.hook.pytest_generate_testsextra=[module]ifclsisnotNone:extra.append(cls())plugins=self.getplugins()+extragentesthook.pcall(plugins,metafunc=metafunc)Function=self._getcustomclass("Function")ifnotmetafunc._calls:returnFunction(name,parent=self)l=[]forcallspecinmetafunc._calls:subname="%s[%s]"%(name,callspec.id)function=Function(name=subname,parent=self,callspec=callspec,callobj=funcobj,keywords={callspec.id:True})l.append(function)returnldeftransfer_markers(funcobj,cls,mod):# XXX this should rather be code in the mark plugin or the mark# plugin should merge with the python plugin.forholderin(cls,mod):try:pytestmark=holder.pytestmarkexceptAttributeError:continueifisinstance(pytestmark,list):formarkinpytestmark:mark(funcobj)else:pytestmark(funcobj)classModule(pytest.File,PyCollectorMixin):def_getobj(self):returnself._memoizedcall('_obj',self._importtestmodule)def_importtestmodule(self):# we assume we are only called once per moduletry:mod=self.fspath.pyimport(ensuresyspath=True)exceptSyntaxError:excinfo=py.code.ExceptionInfo()raiseself.CollectError(excinfo.getrepr(style="short"))exceptself.fspath.ImportMismatchError:e=sys.exc_info()[1]raiseself.CollectError("import file mismatch:\n""imported module %r has this __file__ attribute:\n"" %s\n""which is not the same as the test file we want to collect:\n"" %s\n""HINT: remove __pycache__ / .pyc files and/or use a ""unique basename for your test file modules"%e.args)#print "imported test module", modself.config.pluginmanager.consider_module(mod)returnmoddefsetup(self):ifhasattr(self.obj,'setup_module'):#XXX: nose compat hack, move to nose plugin# if it takes a positional arg, its probably a pytest style one# so we pass the current module objectifinspect.getargspec(self.obj.setup_module)[0]:self.obj.setup_module(self.obj)else:self.obj.setup_module()defteardown(self):ifhasattr(self.obj,'teardown_module'):#XXX: nose compat hack, move to nose plugin# if it takes a positional arg, its probably a py.test style one# so we pass the current module objectifinspect.getargspec(self.obj.teardown_module)[0]:self.obj.teardown_module(self.obj)else:self.obj.teardown_module()classClass(PyCollectorMixin,pytest.Collector):defcollect(self):return[self._getcustomclass("Instance")(name="()",parent=self)]defsetup(self):setup_class=getattr(self.obj,'setup_class',None)ifsetup_classisnotNone:setup_class=getattr(setup_class,'im_func',setup_class)setup_class(self.obj)defteardown(self):teardown_class=getattr(self.obj,'teardown_class',None)ifteardown_classisnotNone:teardown_class=getattr(teardown_class,'im_func',teardown_class)teardown_class(self.obj)classInstance(PyCollectorMixin,pytest.Collector):def_getobj(self):returnself.parent.obj()defnewinstance(self):self.obj=self._getobj()returnself.objclassFunctionMixin(PyobjMixin):""" mixin for the code common to Function and Generator. """defsetup(self):""" perform setup for this test function. """ifhasattr(self,'_preservedparent'):obj=self._preservedparentelifisinstance(self.parent,Instance):obj=self.parent.newinstance()self.obj=self._getobj()else:obj=self.parent.objifinspect.ismethod(self.obj):name='setup_method'else:name='setup_function'setup_func_or_method=getattr(obj,name,None)ifsetup_func_or_methodisnotNone:setup_func_or_method(self.obj)defteardown(self):""" perform teardown for this test function. """ifinspect.ismethod(self.obj):name='teardown_method'else:name='teardown_function'obj=self.parent.objteardown_func_or_meth=getattr(obj,name,None)ifteardown_func_or_methisnotNone:teardown_func_or_meth(self.obj)def_prunetraceback(self,excinfo):ifhasattr(self,'_obj')andnotself.config.option.fulltrace:code=py.code.Code(self.obj)path,firstlineno=code.path,code.firstlinenotraceback=excinfo.tracebackntraceback=traceback.cut(path=path,firstlineno=firstlineno)ifntraceback==traceback:ntraceback=ntraceback.cut(path=path)ifntraceback==traceback:ntraceback=ntraceback.cut(excludepath=cutdir)excinfo.traceback=ntraceback.filter()def_repr_failure_py(self,excinfo,style="long"):ifexcinfo.errisinstance(FuncargRequest.LookupError):fspath,lineno,msg=self.reportinfo()lines,_=inspect.getsourcelines(self.obj)fori,lineinenumerate(lines):ifline.strip().startswith('def'):returnFuncargLookupErrorRepr(fspath,lineno,lines[:i+1],str(excinfo.value))ifexcinfo.errisinstance(pytest.fail.Exception):ifnotexcinfo.value.pytrace:returnstr(excinfo.value)returnsuper(FunctionMixin,self)._repr_failure_py(excinfo,style=style)defrepr_failure(self,excinfo,outerr=None):assertouterrisNone,"XXX outerr usage is deprecated"returnself._repr_failure_py(excinfo,style=self.config.option.tbstyle)classFuncargLookupErrorRepr(TerminalRepr):def__init__(self,filename,firstlineno,deflines,errorstring):self.deflines=deflinesself.errorstring=errorstringself.filename=filenameself.firstlineno=firstlinenodeftoterminal(self,tw):tw.line()forlineinself.deflines:tw.line(" "+line.strip())forlineinself.errorstring.split("\n"):tw.line(" "+line.strip(),red=True)tw.line()tw.line("%s:%d"%(self.filename,self.firstlineno+1))classGenerator(FunctionMixin,PyCollectorMixin,pytest.Collector):defcollect(self):# test generators are seen as collectors but they also# invoke setup/teardown on popular request# (induced by the common "test_*" naming shared with normal tests)self.session._setupstate.prepare(self)# see FunctionMixin.setup and test_setupstate_is_preserved_134self._preservedparent=self.parent.objl=[]seen={}fori,xinenumerate(self.obj()):name,call,args=self.getcallargs(x)ifnotpy.builtin.callable(call):raiseTypeError("%r yielded non callable test %r"%(self.obj,call,))ifnameisNone:name="[%d]"%ielse:name="['%s']"%nameifnameinseen:raiseValueError("%r generated tests with non-unique name %r"%(self,name))seen[name]=Truel.append(self.Function(name,self,args=args,callobj=call))returnldefgetcallargs(self,obj):ifnotisinstance(obj,(tuple,list)):obj=(obj,)# explict namingifisinstance(obj[0],py.builtin._basestring):name=obj[0]obj=obj[1:]else:name=Nonecall,args=obj[0],obj[1:]returnname,call,args## Test Items#_dummy=object()classFunction(FunctionMixin,pytest.Item):""" a Function Item is responsible for setting up and executing a Python callable test object. """_genid=Nonedef__init__(self,name,parent=None,args=None,config=None,callspec=None,callobj=_dummy,keywords=None,session=None):super(Function,self).__init__(name,parent,config=config,session=session)self._args=argsifself._isyieldedfunction():assertnotcallspec,("yielded functions (deprecated) cannot have funcargs")else:ifcallspecisnotNone:self.callspec=callspecself.funcargs=callspec.funcargsor{}self._genid=callspec.idifhasattr(callspec,"param"):self._requestparam=callspec.paramelse:self.funcargs={}ifcallobjisnot_dummy:self._obj=callobjself.function=getattr(self.obj,'im_func',self.obj)self.keywords.update(py.builtin._getfuncdict(self.obj)or{})ifkeywords:self.keywords.update(keywords)def_getobj(self):name=self.namei=name.find("[")# parametrizationifi!=-1:name=name[:i]returngetattr(self.parent.obj,name)def_isyieldedfunction(self):returnself._argsisnotNonedefruntest(self):""" execute the underlying test function. """self.ihook.pytest_pyfunc_call(pyfuncitem=self)defsetup(self):super(Function,self).setup()ifhasattr(self,'funcargs'):fillfuncargs(self)def__eq__(self,other):try:return(self.name==other.nameandself._args==other._argsandself.parent==other.parentandself.obj==other.objandgetattr(self,'_genid',None)==getattr(other,'_genid',None))exceptAttributeError:passreturnFalsedef__ne__(self,other):returnnotself==otherdef__hash__(self):returnhash((self.parent,self.name))defhasinit(obj):init=getattr(obj,'__init__',None)ifinit:ifinit!=object.__init__:returnTruedefgetfuncargnames(function,startindex=None):# XXX merge with main.py's varnamesargnames=py.std.inspect.getargs(py.code.getrawcode(function))[0]ifstartindexisNone:startindex=py.std.inspect.ismethod(function)and1or0defaults=getattr(function,'func_defaults',getattr(function,'__defaults__',None))or()numdefaults=len(defaults)ifnumdefaults:returnargnames[startindex:-numdefaults]returnargnames[startindex:]deffillfuncargs(function):""" fill missing funcargs. """request=FuncargRequest(pyfuncitem=function)request._fillfuncargs()_notexists=object()classCallSpec2(object):def__init__(self,metafunc):self.metafunc=metafuncself.funcargs={}self._idlist=[]self.params={}self._globalid=_notexistsself._globalid_args=set()self._globalparam=_notexistsdefcopy(self,metafunc):cs=CallSpec2(self.metafunc)cs.funcargs.update(self.funcargs)cs.params.update(self.params)cs._idlist=list(self._idlist)cs._globalid=self._globalidcs._globalid_args=self._globalid_argscs._globalparam=self._globalparamreturncsdef_checkargnotcontained(self,arg):ifarginself.paramsorarginself.funcargs:raiseValueError("duplicate %r"%(arg,))defgetparam(self,name):try:returnself.params[name]exceptKeyError:ifself._globalparamis_notexists:raiseValueError(name)returnself._globalparam@propertydefid(self):return"-".join(map(str,filter(None,self._idlist)))defsetmulti(self,valtype,argnames,valset,id):forarg,valinzip(argnames,valset):self._checkargnotcontained(arg)getattr(self,valtype)[arg]=valself._idlist.append(id)defsetall(self,funcargs,id,param):forxinfuncargs:self._checkargnotcontained(x)self.funcargs.update(funcargs)ifidisnot_notexists:self._idlist.append(id)ifparamisnot_notexists:assertself._globalparamis_notexistsself._globalparam=paramclassMetafunc:def__init__(self,function,config=None,cls=None,module=None):self.config=configself.module=moduleself.function=functionself.funcargnames=getfuncargnames(function,startindex=int(clsisnotNone))self.cls=clsself.module=moduleself._calls=[]self._ids=py.builtin.set()defparametrize(self,argnames,argvalues,indirect=False,ids=None):""" Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources you may pass indirect=True and implement a funcarg factory which can perform the expensive setup just before a test is actually run. :arg argnames: an argument name or a list of argument names :arg argvalues: a list of values for the argname or a list of tuples of values for the list of argument names. :arg indirect: if True each argvalue corresponding to an argument will be passed as request.param to its respective funcarg factory so that it can perform more expensive setups during the setup phase of a test rather than at collection time. :arg ids: list of string ids each corresponding to the argvalues so that they are part of the test id. If no ids are provided they will be generated automatically from the argvalues. """ifnotisinstance(argnames,(tuple,list)):argnames=(argnames,)argvalues=[(val,)forvalinargvalues]ifnotindirect:#XXX should we also check for the opposite case?forarginargnames:ifargnotinself.funcargnames:raiseValueError("%r has no argument %r"%(self.function,arg))valtype=indirectand"params"or"funcargs"ifnotids:idmaker=IDMaker()ids=list(map(idmaker,argvalues))newcalls=[]forcallspecinself._callsor[CallSpec2(self)]:fori,valsetinenumerate(argvalues):assertlen(valset)==len(argnames)newcallspec=callspec.copy(self)newcallspec.setmulti(valtype,argnames,valset,ids[i])newcalls.append(newcallspec)self._calls=newcallsdefaddcall(self,funcargs=None,id=_notexists,param=_notexists):""" (deprecated, use parametrize) Add a new call to the underlying test function during the collection phase of a test run. Note that request.addcall() is called during the test collection phase prior and independently to actual test execution. You should only use addcall() if you need to specify multiple arguments of a test function. :arg funcargs: argument keyword dictionary used when invoking the test function. :arg id: used for reporting and identification purposes. If you don't supply an `id` an automatic unique id will be generated. :arg param: a parameter which will be exposed to a later funcarg factory invocation through the ``request.param`` attribute. """assertfuncargsisNoneorisinstance(funcargs,dict)iffuncargsisnotNone:fornameinfuncargs:ifnamenotinself.funcargnames:pytest.fail("funcarg %r not used in this function."%name)else:funcargs={}ifidisNone:raiseValueError("id=None not allowed")ifidis_notexists:id=len(self._calls)id=str(id)ifidinself._ids:raiseValueError("duplicate id %r"%id)self._ids.add(id)cs=CallSpec2(self)cs.setall(funcargs,id,param)self._calls.append(cs)classIDMaker:def__init__(self):self.counter=0def__call__(self,valset):l=[]forvalinvalset:ifnotisinstance(val,(int,str)):val="."+str(self.counter)self.counter+=1l.append(str(val))return"-".join(l)classFuncargRequest:""" A request for function arguments from a test function. Note that there is an optional ``param`` attribute in case there was an invocation to metafunc.addcall(param=...). If no such call was done in a ``pytest_generate_tests`` hook, the attribute will not be present. """_argprefix="pytest_funcarg__"_argname=NoneclassLookupError(LookupError):""" error on performing funcarg request. """def__init__(self,pyfuncitem):self._pyfuncitem=pyfuncitemifhasattr(pyfuncitem,'_requestparam'):self.param=pyfuncitem._requestparamextra=[objforobjin(self.module,self.instance)ifobj]self._plugins=pyfuncitem.getplugins()+extraself._funcargs=self._pyfuncitem.funcargs.copy()self._name2factory={}self._currentarg=None@propertydeffunction(self):""" function object of the test invocation. """returnself._pyfuncitem.obj@propertydefkeywords(self):""" keywords of the test function item. .. versionadded:: 2.0 """returnself._pyfuncitem.keywords@propertydefmodule(self):""" module where the test function was collected. """returnself._pyfuncitem.getparent(pytest.Module).obj@propertydefcls(self):""" class (can be None) where the test function was collected. """clscol=self._pyfuncitem.getparent(pytest.Class)ifclscol:returnclscol.obj@propertydefinstance(self):""" instance (can be None) on which test function was collected. """returnpy.builtin._getimself(self.function)@propertydefconfig(self):""" the pytest config object associated with this request. """returnself._pyfuncitem.config@propertydeffspath(self):""" the file system path of the test module which collected this test. """returnself._pyfuncitem.fspathdef_fillfuncargs(self):argnames=getfuncargnames(self.function)ifargnames:assertnotgetattr(self._pyfuncitem,'_args',None),("yielded functions cannot have funcargs")forargnameinargnames:ifargnamenotinself._pyfuncitem.funcargs:self._pyfuncitem.funcargs[argname]=self.getfuncargvalue(argname)defapplymarker(self,marker):""" Apply a marker to a single test function invocation. This method is useful if you don't want to have a keyword/marker on all function invocations. :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object created by a call to ``py.test.mark.NAME(...)``. """ifnotisinstance(marker,py.test.mark.XYZ.__class__):raiseValueError("%r is not a py.test.mark.* object")self._pyfuncitem.keywords[marker.markname]=markerdefcached_setup(self,setup,teardown=None,scope="module",extrakey=None):""" Return a testing resource managed by ``setup`` & ``teardown`` calls. ``scope`` and ``extrakey`` determine when the ``teardown`` function will be called so that subsequent calls to ``setup`` would recreate the resource. :arg teardown: function receiving a previously setup resource. :arg setup: a no-argument function creating a resource. :arg scope: a string value out of ``function``, ``class``, ``module`` or ``session`` indicating the caching lifecycle of the resource. :arg extrakey: added to internal caching key of (funcargname, scope). """ifnothasattr(self.config,'_setupcache'):self.config._setupcache={}# XXX weakref?cachekey=(self._currentarg,self._getscopeitem(scope),extrakey)cache=self.config._setupcachetry:val=cache[cachekey]exceptKeyError:val=setup()cache[cachekey]=valifteardownisnotNone:deffinalizer():delcache[cachekey]teardown(val)self._addfinalizer(finalizer,scope=scope)returnvaldefgetfuncargvalue(self,argname):""" Retrieve a function argument by name for this test function invocation. This allows one function argument factory to call another function argument factory. If there are two funcarg factories for the same test function argument the first factory may use ``getfuncargvalue`` to call the second one and do something additional with the resource. """try:returnself._funcargs[argname]exceptKeyError:passifargnamenotinself._name2factory:self._name2factory[argname]=self.config.pluginmanager.listattr(plugins=self._plugins,attrname=self._argprefix+str(argname))#else: we are called recursivelyifnotself._name2factory[argname]:self._raiselookupfailed(argname)funcargfactory=self._name2factory[argname].pop()oldarg=self._currentargmp=monkeypatch()mp.setattr(self,'_currentarg',argname)try:param=self._pyfuncitem.callspec.getparam(argname)except(AttributeError,ValueError):passelse:mp.setattr(self,'param',param,raising=False)try:self._funcargs[argname]=res=funcargfactory(request=self)finally:mp.undo()returnresdef_getscopeitem(self,scope):ifscope=="function":returnself._pyfuncitemelifscope=="session":returnNoneelifscope=="class":x=self._pyfuncitem.getparent(pytest.Class)ifxisnotNone:returnxscope="module"ifscope=="module":returnself._pyfuncitem.getparent(pytest.Module)raiseValueError("unknown finalization scope %r"%(scope,))defaddfinalizer(self,finalizer):"""add finalizer function to be called after test function finished execution. """self._addfinalizer(finalizer,scope="function")def_addfinalizer(self,finalizer,scope):colitem=self._getscopeitem(scope)self._pyfuncitem.session._setupstate.addfinalizer(finalizer=finalizer,colitem=colitem)def__repr__(self):return"<FuncargRequest for %r>"%(self._pyfuncitem)def_raiselookupfailed(self,argname):available=[]forplugininself._plugins:fornameinvars(plugin):ifname.startswith(self._argprefix):name=name[len(self._argprefix):]ifnamenotinavailable:available.append(name)fspath,lineno,msg=self._pyfuncitem.reportinfo()msg="LookupError: no factory found for function argument %r"%(argname,)msg+="\n available funcargs: %s"%(", ".join(available),)msg+="\n use 'py.test --funcargs [testpath]' for help on them."raiseself.LookupError(msg)defshowfuncargs(config):from_pytest.mainimportwrap_sessionreturnwrap_session(config,_showfuncargs_main)def_showfuncargs_main(config,session):session.perform_collect()ifsession.items:plugins=session.items[0].getplugins()else:plugins=session.getplugins()curdir=py.path.local()tw=py.io.TerminalWriter()verbose=config.getvalue("verbose")forplugininplugins:available=[]forname,factoryinvars(plugin).items():ifname.startswith(FuncargRequest._argprefix):name=name[len(FuncargRequest._argprefix):]ifnamenotinavailable:available.append([name,factory])ifavailable:pluginname=plugin.__name__forname,factoryinavailable:loc=getlocation(factory,curdir)ifverbose:funcargspec="%s -- %s"%(name,loc,)else:funcargspec=nametw.line(funcargspec,green=True)doc=factory.__doc__or""ifdoc:forlineindoc.split("\n"):tw.line(" "+line.strip())else:tw.line(" %s: no docstring available"%(loc,),red=True)defgetlocation(function,curdir):importinspectfn=py.path.local(inspect.getfile(function))lineno=py.builtin._getcode(function).co_firstlinenoiffn.relto(curdir):fn=fn.relto(curdir)return"%s:%d"%(fn,lineno+1)# builtin pytest.raises helperdefraises(ExpectedException,*args,**kwargs):""" assert that a code block/function call raises @ExpectedException and raise a failure exception otherwise. If using Python 2.5 or above, you may use this function as a context manager:: >>> with raises(ZeroDivisionError): ... 1/0 Or you can specify a callable by passing a to-be-called lambda:: >>> raises(ZeroDivisionError, lambda: 1/0) <ExceptionInfo ...> or you can specify an arbitrary callable with arguments:: >>> def f(x): return 1/x ... >>> raises(ZeroDivisionError, f, 0) <ExceptionInfo ...> >>> raises(ZeroDivisionError, f, x=0) <ExceptionInfo ...> A third possibility is to use a string to be executed:: >>> raises(ZeroDivisionError, "f(0)") <ExceptionInfo ...> """__tracebackhide__=Trueifnotargs:returnRaisesContext(ExpectedException)elifisinstance(args[0],str):code,=argsassertisinstance(code,str)frame=sys._getframe(1)loc=frame.f_locals.copy()loc.update(kwargs)#print "raises frame scope: %r" % frame.f_localstry:code=py.code.Source(code).compile()py.builtin.exec_(code,frame.f_globals,loc)# XXX didn'T mean f_globals == f_locals something special?# this is destroyed here ...exceptExpectedException:returnpy.code.ExceptionInfo()else:func=args[0]try:func(*args[1:],**kwargs)exceptExpectedException:returnpy.code.ExceptionInfo()k=", ".join(["%s=%r"%xforxinkwargs.items()])ifk:k=', '+kexpr='%s(%r%s)'%(getattr(func,'__name__',func),args,k)pytest.fail("DID NOT RAISE")classRaisesContext(object):def__init__(self,ExpectedException):self.ExpectedException=ExpectedExceptionself.excinfo=Nonedef__enter__(self):self.excinfo=object.__new__(py.code.ExceptionInfo)returnself.excinfodef__exit__(self,*tp):__tracebackhide__=Trueiftp[0]isNone:pytest.fail("DID NOT RAISE")self.excinfo.__init__(tp)returnissubclass(self.excinfo.type,self.ExpectedException)