284 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
from __future__ import generators
 | 
						|
 | 
						|
import py
 | 
						|
from conftesthandle import Conftest
 | 
						|
from py.__.test.defaultconftest import adddefaultoptions
 | 
						|
 | 
						|
optparse = py.compat.optparse
 | 
						|
 | 
						|
# XXX move to Config class
 | 
						|
basetemp = None
 | 
						|
def ensuretemp(string, dir=1): 
 | 
						|
    """ return temporary directory path with
 | 
						|
        the given string as the trailing part. 
 | 
						|
    """ 
 | 
						|
    global basetemp
 | 
						|
    if basetemp is None: 
 | 
						|
        basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
 | 
						|
    return basetemp.ensure(string, dir=dir) 
 | 
						|
  
 | 
						|
class CmdOptions(object):
 | 
						|
    """ pure container instance for holding cmdline options 
 | 
						|
        as attributes. 
 | 
						|
    """
 | 
						|
    def __repr__(self):
 | 
						|
        return "<CmdOptions %r>" %(self.__dict__,)
 | 
						|
 | 
						|
class Config(object): 
 | 
						|
    """ central hub for dealing with configuration/initialization data. """ 
 | 
						|
    Option = optparse.Option
 | 
						|
 | 
						|
    def __init__(self): 
 | 
						|
        self.option = CmdOptions()
 | 
						|
        self._parser = optparse.OptionParser(
 | 
						|
            usage="usage: %prog [options] [query] [filenames of tests]")
 | 
						|
        self.conftest = Conftest()
 | 
						|
        self._initialized = False
 | 
						|
        self._overwrite_dict = {}
 | 
						|
 | 
						|
    def parse(self, args): 
 | 
						|
        """ parse cmdline arguments into this config object. 
 | 
						|
            Note that this can only be called once per testing process. 
 | 
						|
        """ 
 | 
						|
        assert not self._initialized, (
 | 
						|
                "can only parse cmdline args once per Config object")
 | 
						|
        self._initialized = True
 | 
						|
        adddefaultoptions(self)
 | 
						|
        self.conftest.setinitial(args) 
 | 
						|
        args = [str(x) for x in args]
 | 
						|
        cmdlineoption, args = self._parser.parse_args(args) 
 | 
						|
        self.option.__dict__.update(vars(cmdlineoption))
 | 
						|
        if not args:
 | 
						|
            args.append(py.std.os.getcwd())
 | 
						|
        self.topdir = gettopdir(args)
 | 
						|
        self.args = args 
 | 
						|
 | 
						|
    def initdirect(self, topdir, repr, coltrails=None):
 | 
						|
        assert not self._initialized
 | 
						|
        self._initialized = True
 | 
						|
        self.topdir = py.path.local(topdir)
 | 
						|
        self.merge_repr(repr)
 | 
						|
        self._coltrails = coltrails 
 | 
						|
 | 
						|
    def getcolitems(self):
 | 
						|
        """ return initial collectors. """
 | 
						|
        trails = getattr(self, '_coltrails', None)
 | 
						|
        return [self._getcollector(path) for path in (trails or self.args)]
 | 
						|
 | 
						|
    def _getcollector(self, path):
 | 
						|
        if isinstance(path, tuple):
 | 
						|
            relpath, names = path
 | 
						|
            fspath = self.topdir.join(relpath)
 | 
						|
            col = self._getcollector(fspath)
 | 
						|
        else:
 | 
						|
            path = py.path.local(path)
 | 
						|
            assert path.check(), "%s: path does not exist" %(path,)
 | 
						|
            col = self._getrootcollector(path)
 | 
						|
            names = path.relto(col.fspath).split(path.sep)
 | 
						|
        return col._getitembynames(names)
 | 
						|
 | 
						|
    def _getrootcollector(self, path):
 | 
						|
        pkgpath = path.pypkgpath()
 | 
						|
        if pkgpath is None:
 | 
						|
            pkgpath = path.check(file=1) and path.dirpath() or path
 | 
						|
        col = self.conftest.rget("Directory", pkgpath)(pkgpath)
 | 
						|
        col._config = self
 | 
						|
        return col 
 | 
						|
 | 
						|
    def getvalue_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()
 | 
						|
        return [modpath.join(x, abs=True) for x in relroots]
 | 
						|
             
 | 
						|
    def addoptions(self, groupname, *specs): 
 | 
						|
        """ add a named group of options to the current testing session. 
 | 
						|
            This function gets invoked during testing session initialization. 
 | 
						|
        """ 
 | 
						|
        for spec in specs:
 | 
						|
            for shortopt in spec._short_opts:
 | 
						|
                if not shortopt.isupper(): 
 | 
						|
                    raise ValueError(
 | 
						|
                        "custom options must be capital letter "
 | 
						|
                        "got %r" %(spec,)
 | 
						|
                    )
 | 
						|
        return self._addoptions(groupname, *specs)
 | 
						|
 | 
						|
    def _addoptions(self, groupname, *specs):
 | 
						|
        optgroup = optparse.OptionGroup(self._parser, groupname) 
 | 
						|
        optgroup.add_options(specs) 
 | 
						|
        self._parser.add_option_group(optgroup)
 | 
						|
        for opt in specs: 
 | 
						|
            if hasattr(opt, 'default') and opt.dest:
 | 
						|
                setattr(self.option, opt.dest, opt.default) 
 | 
						|
        return self.option
 | 
						|
 | 
						|
    def getvalue(self, name, path=None): 
 | 
						|
        """ return 'name' value looked up 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 self._overwrite_dict[name]
 | 
						|
        except KeyError:
 | 
						|
            return self.conftest.rget(name, path)
 | 
						|
 | 
						|
    def initsession(self):
 | 
						|
        """ return an initialized session object. """
 | 
						|
        cls = self._getsessionclass()
 | 
						|
        session = cls(self)
 | 
						|
        session.fixoptions()
 | 
						|
        return session
 | 
						|
 | 
						|
    def _getsessionclass(self): 
 | 
						|
        """ return Session class determined from cmdline options
 | 
						|
            and looked up in initial config modules. 
 | 
						|
        """
 | 
						|
        if self.option.session is not None:
 | 
						|
            return self.conftest.rget(self.option.session)
 | 
						|
        else:
 | 
						|
            name = self._getsessionname()
 | 
						|
            importpath = globals()[name]
 | 
						|
            mod = __import__(importpath, None, None, '__doc__')
 | 
						|
            return getattr(mod, name)
 | 
						|
 | 
						|
    def _getsessionname(self):
 | 
						|
        """ return default session name as determined from options. """
 | 
						|
        name = 'TerminalSession'
 | 
						|
        if self.option.dist:
 | 
						|
            name = 'RSession'
 | 
						|
        else:
 | 
						|
            optnames = 'startserver runbrowser apigen restreport boxed'.split()
 | 
						|
            for opt in optnames:
 | 
						|
                if getattr(self.option, opt, False):
 | 
						|
                    name = 'LSession'
 | 
						|
                    break
 | 
						|
            else:
 | 
						|
                if self.getvalue('dist_boxed'):
 | 
						|
                    name = 'LSession'
 | 
						|
                if self.option.looponfailing:
 | 
						|
                    name = 'RemoteTerminalSession'
 | 
						|
                elif self.option.executable:
 | 
						|
                    name = 'RemoteTerminalSession'
 | 
						|
        return name
 | 
						|
 | 
						|
    def _reparse(self, args):
 | 
						|
        """ this is used from tests that want to re-invoke parse(). """
 | 
						|
        #assert args # XXX should not be empty
 | 
						|
        global config_per_process
 | 
						|
        oldconfig = py.test.config
 | 
						|
        try:
 | 
						|
            config_per_process = py.test.config = Config()
 | 
						|
            config_per_process.parse(args) 
 | 
						|
            return config_per_process
 | 
						|
        finally: 
 | 
						|
            config_per_process = py.test.config = oldconfig 
 | 
						|
 | 
						|
    def _overwrite(self, name, value):
 | 
						|
        """ this is used from tests to overwrite values irrespectives of conftests.
 | 
						|
        """
 | 
						|
        self._overwrite_dict[name] = value
 | 
						|
 | 
						|
    def make_repr(self, conftestnames, optnames=None): 
 | 
						|
        """ return a marshallable representation 
 | 
						|
            of conftest and cmdline options. 
 | 
						|
            if optnames is None, all options
 | 
						|
            on self.option will be transferred. 
 | 
						|
        """ 
 | 
						|
        conftestdict = {}
 | 
						|
        for name in conftestnames: 
 | 
						|
            value = self.getvalue(name)
 | 
						|
            checkmarshal(name, value)
 | 
						|
            conftestdict[name] = value 
 | 
						|
        cmdlineopts = {}
 | 
						|
        if optnames is None:
 | 
						|
            optnames = dir(self.option)
 | 
						|
        for name in optnames: 
 | 
						|
            if not name.startswith("_"):
 | 
						|
                value = getattr(self.option, name)
 | 
						|
                checkmarshal(name, value)
 | 
						|
                cmdlineopts[name] = value
 | 
						|
        l = []
 | 
						|
        for path in self.args:
 | 
						|
            path = py.path.local(path)
 | 
						|
            l.append(path.relto(self.topdir)) 
 | 
						|
        return l, conftestdict, cmdlineopts
 | 
						|
 | 
						|
    def merge_repr(self, repr): 
 | 
						|
        """ merge in the conftest and cmdline option values
 | 
						|
            found in the given representation (produced
 | 
						|
            by make_repr above).  
 | 
						|
 | 
						|
            The repr-contained conftest values are
 | 
						|
            stored on the default conftest module (last
 | 
						|
            priority) and the cmdline options on self.option. 
 | 
						|
        """
 | 
						|
        class override:
 | 
						|
            def __init__(self, d):
 | 
						|
                self.__dict__.update(d)
 | 
						|
                self.__file__ = "<options from remote>"
 | 
						|
        args, conftestdict, cmdlineopts = repr
 | 
						|
        self.args = [self.topdir.join(x) for x in args]
 | 
						|
        self.conftest.setinitial(self.args)
 | 
						|
        self.conftest._path2confmods[None].append(override(conftestdict))
 | 
						|
        for name, val in cmdlineopts.items(): 
 | 
						|
            setattr(self.option, name, val) 
 | 
						|
 | 
						|
    def get_collector_trail(self, collector):
 | 
						|
        """ provide a trail relative to the topdir, 
 | 
						|
            which can be used to reconstruct the
 | 
						|
            collector (possibly on a different host
 | 
						|
            starting from a different topdir). 
 | 
						|
        """ 
 | 
						|
        chain = collector.listchain()
 | 
						|
        relpath = chain[0].fspath.relto(self.topdir)
 | 
						|
        if not relpath:
 | 
						|
            if chain[0].fspath == self.topdir:
 | 
						|
                relpath = "."
 | 
						|
            else:
 | 
						|
                raise ValueError("%r not relative to %s" 
 | 
						|
                         %(chain[0], self.topdir))
 | 
						|
        return relpath, tuple([x.name for x in chain[1:]])
 | 
						|
 | 
						|
# this is the one per-process instance of py.test configuration 
 | 
						|
config_per_process = Config()
 | 
						|
 | 
						|
# default import paths for sessions 
 | 
						|
 | 
						|
TerminalSession = 'py.__.test.terminal.terminal'
 | 
						|
RemoteTerminalSession = 'py.__.test.terminal.remote'
 | 
						|
RSession = 'py.__.test.rsession.rsession'
 | 
						|
LSession = 'py.__.test.rsession.rsession'
 | 
						|
 | 
						|
#
 | 
						|
# helpers
 | 
						|
#
 | 
						|
 | 
						|
def checkmarshal(name, value):
 | 
						|
    try:
 | 
						|
        py.std.marshal.dumps(value)
 | 
						|
    except ValueError:
 | 
						|
        raise ValueError("%s=%r is not marshallable" %(name, value))
 | 
						|
 | 
						|
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. 
 | 
						|
    """
 | 
						|
    args = [py.path.local(arg) for arg in args]
 | 
						|
    p = reduce(py.path.local.common, args)
 | 
						|
    assert p, "cannot determine common basedir of %s" %(args,)
 | 
						|
    pkgdir = p.pypkgpath()
 | 
						|
    if pkgdir is None:
 | 
						|
        return p
 | 
						|
    else:
 | 
						|
        return pkgdir.dirpath()
 |