From 6040cf158d284880e6cb661f4e8c7039906caadc Mon Sep 17 00:00:00 2001 From: hpk Date: Wed, 24 Jan 2007 17:39:42 +0100 Subject: [PATCH] [svn r37269] merging of file:///svn/py/branch/config/py/test/config.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37263 | hpk | 2007-01-24 14:59:20 +0100 (Wed, 24 Jan 2007) | 19 lines * introduce config.initdirect() which should be the central place to perform initialization on remote nodes/sides. * fix --looponfailing (which works nicely now) * have Conftest() be an instance instead of a class-variable (avoid global state) * more thoroughly use the "config.topdir" concept (which is not neccessarily the pkgdir.dirpath()) * on merge_repr don't write values to global defaultconftest module (this gets tests failing that reuse defaultconftest module, but also we want to avoid global state in general) ------------------------------------------------------------------------ r37243 | hpk | 2007-01-24 00:06:24 +0100 (Wed, 24 Jan 2007) | 2 lines small step-wise cleanups ------------------------------------------------------------------------ r37238 | hpk | 2007-01-23 23:09:57 +0100 (Tue, 23 Jan 2007) | 2 lines why do it complicated? ------------------------------------------------------------------------ r37235 | hpk | 2007-01-23 22:25:16 +0100 (Tue, 23 Jan 2007) | 5 lines added get_collector_trail method to get a representation of a collector relative to the topdir. ------------------------------------------------------------------------ r37230 | hpk | 2007-01-23 20:44:22 +0100 (Tue, 23 Jan 2007) | 2 lines typo-level fixes ------------------------------------------------------------------------ r37193 | hpk | 2007-01-23 14:53:17 +0100 (Tue, 23 Jan 2007) | 5 lines introduced config.topdir, tests and use it from terminal/remote at least. also fix fijal's "--box" option handling a bit, but i am not sure it should stay this way at all. ------------------------------------------------------------------------ r37172 | fijal | 2007-01-23 11:26:10 +0100 (Tue, 23 Jan 2007) | 2 lines Boxing implies lsession ------------------------------------------------------------------------ r37114 | hpk | 2007-01-22 02:20:30 +0100 (Mon, 22 Jan 2007) | 6 lines streamlining initial construction of collectors (to unify topdir/pkgdir/collector handling in rsession/terminal/normal) still work in-progress with a skipped test ------------------------------------------------------------------------ r37112 | hpk | 2007-01-22 01:07:09 +0100 (Mon, 22 Jan 2007) | 3 lines * moving towards a common rootdir/root collector * adding an xxxed test ------------------------------------------------------------------------ r37110 | hpk | 2007-01-22 00:39:24 +0100 (Mon, 22 Jan 2007) | 2 lines renaming some variables for more clarity ------------------------------------------------------------------------ r37077 | hpk | 2007-01-20 22:26:52 +0100 (Sat, 20 Jan 2007) | 2 lines cleaning up fixoptions() a bit ------------------------------------------------------------------------ r37076 | hpk | 2007-01-20 22:19:42 +0100 (Sat, 20 Jan 2007) | 2 lines unskip exec and looponfailing tests, fixes for that ------------------------------------------------------------------------ r37075 | hpk | 2007-01-20 21:51:18 +0100 (Sat, 20 Jan 2007) | 2 lines remove two debug prints ------------------------------------------------------------------------ r37074 | hpk | 2007-01-20 21:43:52 +0100 (Sat, 20 Jan 2007) | 9 lines * config.initsession() deals with getting at a session class and initiliazing it * config._getcollector(path) gets a collector pointing to path * added lots of tests * thus remove the strange getfscollector and map2colitems logic * mark some tkinter tests as skipped * fixing things all around ------------------------------------------------------------------------ r37065 | arigo | 2007-01-20 17:15:10 +0100 (Sat, 20 Jan 2007) | 3 lines Choose a random free port when using the --runbrowser option, but stick to the fixed 8000 when using --startserver only. ------------------------------------------------------------------------ r37061 | hpk | 2007-01-20 16:29:25 +0100 (Sat, 20 Jan 2007) | 8 lines refactoring/fixing the remote terminal approach (used for --exec and --looponfailing) to use the new configiguration. Removes lots of strange code. XXX there are still some recursive testing issues. ------------------------------------------------------------------------ r37055 | hpk | 2007-01-20 12:51:54 +0100 (Sat, 20 Jan 2007) | 4 lines reintroducing --session to lookup custom session objects in conftests.py there is no other way to override a session object from conftests. ------------------------------------------------------------------------ r37025 | hpk | 2007-01-19 17:59:50 +0100 (Fri, 19 Jan 2007) | 2 lines remove lget and its last usage (which was a bit random) ------------------------------------------------------------------------ r37020 | hpk | 2007-01-19 17:48:15 +0100 (Fri, 19 Jan 2007) | 3 lines switching in the new way of getting to a sessionclass, removing code in cmdline.py and elsewhere accordingly ------------------------------------------------------------------------ r37013 | hpk | 2007-01-19 17:14:11 +0100 (Fri, 19 Jan 2007) | 2 lines somewhat simpler lookup mechanism for sessions ------------------------------------------------------------------------ r37009 | hpk | 2007-01-19 16:50:32 +0100 (Fri, 19 Jan 2007) | 2 lines adding a config method to determine the session name ------------------------------------------------------------------------ r36994 | fijal | 2007-01-19 15:08:16 +0100 (Fri, 19 Jan 2007) | 2 lines removal of some trailing white-spaces. ------------------------------------------------------------------------ r36964 | hpk | 2007-01-18 19:30:02 +0100 (Thu, 18 Jan 2007) | 8 lines move rconfig functionality to general Config object, with docstrings, and trying to change rsession accordingly. note that it's called "make_repr" and "merge_repr" the latter to make it obvious that we are changing the instance with the given repr. ------------------------------------------------------------------------ r36942 | hpk | 2007-01-18 16:08:06 +0100 (Thu, 18 Jan 2007) | 4 lines group things used for testing and not use non-underscore names (otherwise they are consdiered public and are possibly seen from apigen through py.test.config!) ------------------------------------------------------------------------ r36941 | fijal | 2007-01-18 15:59:08 +0100 (Thu, 18 Jan 2007) | 2 lines Add a hack which allows to temporarily overwrite the config value. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ --HG-- branch : trunk --- py/test/config.py.merge.tmp | 288 ++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 py/test/config.py.merge.tmp diff --git a/py/test/config.py.merge.tmp b/py/test/config.py.merge.tmp new file mode 100644 index 000000000..91cccee86 --- /dev/null +++ b/py/test/config.py.merge.tmp @@ -0,0 +1,288 @@ +from __future__ import generators + +import py +from conftesthandle import Conftest + +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 "" %(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 + self.conftest.setinitial(args) + self.conftest.rget('adddefaultoptions')() + args = [str(x) for x in args] + cmdlineoption, args = self._parser.parse_args(args) + self.option.__dict__.update(vars(cmdlineoption)) + fixoptions(self.option) # XXX fixing should be moved to sessions + 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 addoptions(self, groupname, *specs): + """ add a named group of options to the current testing session. + This function gets invoked during testing session initialization. + """ + 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' + elif self.option.tkinter: + name = 'TkinterSession' + else: + optnames = 'startserver runbrowser apigen restreport boxing'.split() + for opt in optnames: + if getattr(self.option, opt, False): + name = 'LSession' + break + else: + if self.getvalue('dist_boxing'): + name = 'LSession' + if self.option.looponfailing: + name = 'RemoteTerminalSession' + elif self.option.executable: + name = 'RemoteTerminalSession' + return name + + def is_boxed(self): + # XXX probably not a good idea to have this special function ... + return self.option.boxing or self.getvalue("dist_boxing") + + def _reparse(self, args): + """ this is used from tests that want to re-invoke parse(). """ + 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) + 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 + +TkinterSession = 'py.__.test.tkinter.reportsession' +TerminalSession = 'py.__.test.terminal.terminal' +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 fixoptions(option): + """ sanity checks and making option values canonical. """ + + # implied options + if option.usepdb: + if not option.nocapture: + #print "--pdb implies --nocapture" + option.nocapture = True + + if option.runbrowser and not option.startserver: + #print "--runbrowser implies --startserver" + option.startserver = True + + # conflicting options + if option.looponfailing and option.usepdb: + raise ValueError, "--looponfailing together with --pdb not supported." + if option.looponfailing and option.dist: + raise ValueError, "--looponfailing together with --dist not supported." + if option.executable and option.usepdb: + raise ValueError, "--exec together with --pdb not supported." + + +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()