292 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
| import py, os
 | |
| from py._test.conftesthandle import Conftest
 | |
| from py._test.pluginmanager import PluginManager
 | |
| from py._test import parseopt
 | |
| from py._test.collect import RootCollector
 | |
| 
 | |
| def ensuretemp(string, dir=1): 
 | |
|     """ (deprecated) return temporary directory path with
 | |
|         the given string as the trailing part.  It is usually 
 | |
|         better to use the 'tmpdir' function argument which will
 | |
|         take care to provide empty unique directories for each 
 | |
|         test call even if the test is called multiple times. 
 | |
|     """ 
 | |
|     #py.log._apiwarn(">1.1", "use tmpdir function argument")
 | |
|     return py.test.config.ensuretemp(string, dir=dir)
 | |
|   
 | |
| class CmdOptions(object):
 | |
|     """ holds cmdline options as attributes."""
 | |
|     def __init__(self, **kwargs):
 | |
|         self.__dict__.update(kwargs)
 | |
|     def __repr__(self):
 | |
|         return "<CmdOptions %r>" %(self.__dict__,)
 | |
| 
 | |
| class Error(Exception):
 | |
|     """ Test Configuration Error. """
 | |
| 
 | |
| class Config(object): 
 | |
|     """ access to config values, pluginmanager and plugin hooks.  """
 | |
|     Option = py.std.optparse.Option 
 | |
|     Error = Error
 | |
|     basetemp = None
 | |
|     _sessionclass = None
 | |
| 
 | |
|     def __init__(self, topdir=None, option=None): 
 | |
|         self.option = option or CmdOptions()
 | |
|         self.topdir = topdir
 | |
|         self._parser = parseopt.Parser(
 | |
|             usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
 | |
|             processopt=self._processopt,
 | |
|         )
 | |
|         self.pluginmanager = PluginManager()
 | |
|         self._conftest = Conftest(onimport=self._onimportconftest)
 | |
|         self.hook = self.pluginmanager.hook
 | |
| 
 | |
|     def _onimportconftest(self, conftestmodule):
 | |
|         self.trace("loaded conftestmodule %r" %(conftestmodule,))
 | |
|         self.pluginmanager.consider_conftest(conftestmodule)
 | |
| 
 | |
|     def _getmatchingplugins(self, fspath):
 | |
|         allconftests = self._conftest._conftestpath2mod.values()
 | |
|         plugins = [x for x in self.pluginmanager.getplugins() 
 | |
|                         if x not in allconftests]
 | |
|         plugins += self._conftest.getconftestmodules(fspath)
 | |
|         return plugins
 | |
| 
 | |
|     def trace(self, msg):
 | |
|         if getattr(self.option, 'traceconfig', None):
 | |
|             self.hook.pytest_trace(category="config", msg=msg)
 | |
| 
 | |
|     def _processopt(self, opt):
 | |
|         if hasattr(opt, 'default') and opt.dest:
 | |
|             val = os.environ.get("PYTEST_OPTION_" + opt.dest.upper(), None)
 | |
|             if val is not None:
 | |
|                 if opt.type == "int":
 | |
|                     val = int(val)
 | |
|                 elif opt.type == "long":
 | |
|                     val = long(val)
 | |
|                 elif opt.type == "float":
 | |
|                     val = float(val)
 | |
|                 elif not opt.type and opt.action in ("store_true", "store_false"):
 | |
|                     val = eval(val)
 | |
|                 opt.default = val 
 | |
|             else:
 | |
|                 name = "option_" + opt.dest
 | |
|                 try:
 | |
|                     opt.default = self._conftest.rget(name)
 | |
|                 except (ValueError, KeyError):
 | |
|                     pass
 | |
|             if not hasattr(self.option, opt.dest):
 | |
|                 setattr(self.option, opt.dest, opt.default)
 | |
| 
 | |
|     def _preparse(self, args):
 | |
|         self.pluginmanager.consider_setuptools_entrypoints()
 | |
|         self.pluginmanager.consider_env()
 | |
|         self.pluginmanager.consider_preparse(args)
 | |
|         self._conftest.setinitial(args) 
 | |
|         self.pluginmanager.do_addoption(self._parser)
 | |
| 
 | |
|     def parse(self, args): 
 | |
|         """ parse cmdline arguments into this config object. 
 | |
|             Note that this can only be called once per testing process. 
 | |
|         """ 
 | |
|         assert not hasattr(self, 'args'), (
 | |
|                 "can only parse cmdline args at most once per Config object")
 | |
|         self._preparse(args)
 | |
|         self._parser.hints.extend(self.pluginmanager._hints)
 | |
|         args = self._parser.parse_setoption(args, self.option)
 | |
|         if not args:
 | |
|             args.append(py.std.os.getcwd())
 | |
|         self.topdir = gettopdir(args)
 | |
|         self._rootcol = RootCollector(config=self)
 | |
|         self._setargs(args)
 | |
| 
 | |
|     def _setargs(self, args):
 | |
|         self.args = list(args)
 | |
|         self._argfspaths = [py.path.local(decodearg(x)[0]) for x in args]
 | |
| 
 | |
|     # config objects are usually pickled across system
 | |
|     # barriers but they contain filesystem paths. 
 | |
|     # upon getstate/setstate we take care to do everything
 | |
|     # relative to "topdir". 
 | |
|     def __getstate__(self):
 | |
|         l = []
 | |
|         for path in self.args:
 | |
|             path = py.path.local(path)
 | |
|             l.append(path.relto(self.topdir)) 
 | |
|         return l, self.option.__dict__
 | |
| 
 | |
|     def __setstate__(self, repr):
 | |
|         # we have to set py.test.config because loading 
 | |
|         # of conftest files may use it (deprecated) 
 | |
|         # mainly by py.test.config.addoptions() 
 | |
|         global config_per_process
 | |
|         py.test.config = config_per_process = self 
 | |
|         args, cmdlineopts = repr 
 | |
|         cmdlineopts = CmdOptions(**cmdlineopts)
 | |
|         # next line will registers default plugins 
 | |
|         self.__init__(topdir=py.path.local(), option=cmdlineopts)
 | |
|         self._rootcol = RootCollector(config=self)
 | |
|         args = [str(self.topdir.join(x)) for x in args]
 | |
|         self._preparse(args)
 | |
|         self._setargs(args)
 | |
| 
 | |
|     def ensuretemp(self, string, dir=True):
 | |
|         return self.getbasetemp().ensure(string, dir=dir) 
 | |
| 
 | |
|     def getbasetemp(self):
 | |
|         if self.basetemp is None:
 | |
|             basetemp = self.option.basetemp 
 | |
|             if basetemp:
 | |
|                 basetemp = py.path.local(basetemp)
 | |
|                 if not basetemp.check(dir=1):
 | |
|                     basetemp.mkdir()
 | |
|             else:
 | |
|                 basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
 | |
|             self.basetemp = basetemp
 | |
|         return self.basetemp 
 | |
| 
 | |
|     def mktemp(self, basename, numbered=False):
 | |
|         basetemp = self.getbasetemp()
 | |
|         if not numbered:
 | |
|             return basetemp.mkdir(basename)
 | |
|         else:
 | |
|             return py.path.local.make_numbered_dir(prefix=basename,
 | |
|                 keep=0, rootdir=basetemp, lock_timeout=None)
 | |
| 
 | |
|     def getinitialnodes(self):
 | |
|         return [self.getnode(arg) for arg in self.args]
 | |
| 
 | |
|     def getnode(self, arg):
 | |
|         parts = decodearg(arg)
 | |
|         path = py.path.local(parts.pop(0))
 | |
|         if not path.check():
 | |
|             raise self.Error("file not found: %s" %(path,))
 | |
|         topdir = self.topdir
 | |
|         if path != topdir and not path.relto(topdir):
 | |
|             raise self.Error("path %r is not relative to %r" %
 | |
|                 (str(path), str(topdir)))
 | |
|         # assumtion: pytest's fs-collector tree follows the filesystem tree
 | |
|         names = list(filter(None, path.relto(topdir).split(path.sep)))
 | |
|         names += parts
 | |
|         try:
 | |
|             return self._rootcol.getbynames(names)
 | |
|         except ValueError:
 | |
|             e = py.std.sys.exc_info()[1]
 | |
|             raise self.Error("can't collect: %s\n%s" % (arg, e.args[0]))
 | |
| 
 | |
|     def _getcollectclass(self, name, path):
 | |
|         try:
 | |
|             cls = self._conftest.rget(name, path)
 | |
|         except KeyError:
 | |
|             return getattr(py.test.collect, name)
 | |
|         else:
 | |
|             py.log._apiwarn(">1.1", "%r was found in a conftest.py file, "
 | |
|                 "use pytest_collect hooks instead." % (cls,))
 | |
|             return cls
 | |
| 
 | |
|     def getconftest_pathlist(self, name, path=None):
 | |
|         """ return a matching value, which needs to be sequence
 | |
|             of filenames that will be returned as a list of Path
 | |
|             objects (they can be relative to the location 
 | |
|             where they were found).
 | |
|         """
 | |
|         try:
 | |
|             mod, relroots = self._conftest.rget_with_confmod(name, path)
 | |
|         except KeyError:
 | |
|             return None
 | |
|         modpath = py.path.local(mod.__file__).dirpath()
 | |
|         l = []
 | |
|         for relroot in relroots:
 | |
|             if not isinstance(relroot, py.path.local):
 | |
|                 relroot = relroot.replace("/", py.path.local.sep)
 | |
|                 relroot = modpath.join(relroot, abs=True)
 | |
|             l.append(relroot)
 | |
|         return l 
 | |
|              
 | |
|     def addoptions(self, groupname, *specs): 
 | |
|         """ add a named group of options to the current testing session. 
 | |
|             This function gets invoked during testing session initialization. 
 | |
|         """ 
 | |
|         py.log._apiwarn("1.0", "define pytest_addoptions(parser) to add options", stacklevel=2)
 | |
|         group = self._parser.getgroup(groupname)
 | |
|         for opt in specs:
 | |
|             group._addoption_instance(opt)
 | |
|         return self.option 
 | |
| 
 | |
|     def addoption(self, *optnames, **attrs):
 | |
|         return self._parser.addoption(*optnames, **attrs)
 | |
| 
 | |
|     def getvalueorskip(self, name, path=None): 
 | |
|         """ return getvalue() or call py.test.skip if no value exists. """
 | |
|         try:
 | |
|             val = self.getvalue(name, path)
 | |
|             if val is None:
 | |
|                 raise KeyError(name)
 | |
|             return val
 | |
|         except KeyError:
 | |
|             py.test.skip("no %r value found" %(name,))
 | |
| 
 | |
|     def getvalue(self, name, path=None): 
 | |
|         """ return 'name' value looked up from the 'options'
 | |
|             and then from the first conftest file found up 
 | |
|             the path (including the path itself). 
 | |
|             if path is None, lookup the value in the initial
 | |
|             conftest modules found during command line parsing. 
 | |
|         """
 | |
|         try:
 | |
|             return getattr(self.option, name)
 | |
|         except AttributeError:
 | |
|             return self._conftest.rget(name, path)
 | |
| 
 | |
|     def setsessionclass(self, cls):
 | |
|         if self._sessionclass is not None:
 | |
|             raise ValueError("sessionclass already set to: %r" %(
 | |
|                 self._sessionclass))
 | |
|         self._sessionclass = cls
 | |
| 
 | |
|     def initsession(self):
 | |
|         """ return an initialized session object. """
 | |
|         cls = self._sessionclass 
 | |
|         if cls is None:
 | |
|             from py._test.session import Session
 | |
|             cls = Session
 | |
|         session = cls(self)
 | |
|         self.trace("instantiated session %r" % session)
 | |
|         return session
 | |
| 
 | |
| #
 | |
| # helpers
 | |
| #
 | |
| 
 | |
| def gettopdir(args): 
 | |
|     """ return the top directory for the given paths.
 | |
|         if the common base dir resides in a python package 
 | |
|         parent directory of the root package is returned. 
 | |
|     """
 | |
|     fsargs = [py.path.local(decodearg(arg)[0]) for arg in args]
 | |
|     p = fsargs and fsargs[0] or None
 | |
|     for x in fsargs[1:]:
 | |
|         p = p.common(x)
 | |
|     assert p, "cannot determine common basedir of %s" %(fsargs,)
 | |
|     pkgdir = p.pypkgpath()
 | |
|     if pkgdir is None:
 | |
|         if p.check(file=1):
 | |
|             p = p.dirpath()
 | |
|         return p
 | |
|     else:
 | |
|         return pkgdir.dirpath()
 | |
| 
 | |
| def decodearg(arg):
 | |
|     arg = str(arg)
 | |
|     return arg.split("::")
 | |
| 
 | |
| def onpytestaccess():
 | |
|     # it's enough to have our containing module loaded as 
 | |
|     # it initializes a per-process config instance
 | |
|     # which loads default plugins which add to py.test.*
 | |
|     pass 
 | |
| 
 | |
| # a default per-process instance of py.test configuration 
 | |
| config_per_process = Config()
 |