diff --git a/py/_test/config.py b/py/_test/config.py index 47ffd4f09..32bda8634 100644 --- a/py/_test/config.py +++ b/py/_test/config.py @@ -1,8 +1,126 @@ import py, os -from py._test.conftesthandle import Conftest from py._test.pluginmanager import PluginManager from py._test import parseopt +class Conftest(object): + """ the single place for accessing values and interacting + towards conftest modules from py.test objects. + """ + def __init__(self, onimport=None, confcutdir=None): + self._path2confmods = {} + self._onimport = onimport + self._conftestpath2mod = {} + self._confcutdir = confcutdir + self._md5cache = {} + + def setinitial(self, args): + """ try to find a first anchor path for looking up global values + from conftests. This function is usually called _before_ + argument parsing. conftest files may add command line options + and we thus have no completely safe way of determining + which parts of the arguments are actually related to options + and which are file system paths. We just try here to get + bootstrapped ... + """ + current = py.path.local() + opt = '--confcutdir' + for i in range(len(args)): + opt1 = str(args[i]) + if opt1.startswith(opt): + if opt1 == opt: + if len(args) > i: + p = current.join(args[i+1], abs=True) + elif opt1.startswith(opt + "="): + p = current.join(opt1[len(opt)+1:], abs=1) + self._confcutdir = p + break + for arg in args + [current]: + if hasattr(arg, 'startswith') and arg.startswith("--"): + continue + anchor = current.join(arg, abs=1) + if anchor.check(): # we found some file object + self._path2confmods[None] = self.getconftestmodules(anchor) + # let's also consider test* dirs + if anchor.check(dir=1): + for x in anchor.listdir("test*"): + if x.check(dir=1): + self.getconftestmodules(x) + break + else: + assert 0, "no root of filesystem?" + + def getconftestmodules(self, path): + """ return a list of imported conftest modules for the given path. """ + try: + clist = self._path2confmods[path] + except KeyError: + if path is None: + raise ValueError("missing default confest.") + dp = path.dirpath() + clist = [] + if dp != path: + cutdir = self._confcutdir + if cutdir and path != cutdir and not path.relto(cutdir): + pass + else: + conftestpath = path.join("conftest.py") + if conftestpath.check(file=1): + key = conftestpath.computehash() + # XXX logging about conftest loading + if key not in self._md5cache: + clist.append(self.importconftest(conftestpath)) + self._md5cache[key] = conftestpath + else: + # use some kind of logging + print ("WARN: not loading %s" % conftestpath) + clist[:0] = self.getconftestmodules(dp) + self._path2confmods[path] = clist + # be defensive: avoid changes from caller side to + # affect us by always returning a copy of the actual list + return clist[:] + + def rget(self, name, path=None): + mod, value = self.rget_with_confmod(name, path) + return value + + def rget_with_confmod(self, name, path=None): + modules = self.getconftestmodules(path) + modules.reverse() + for mod in modules: + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def importconftest(self, conftestpath): + assert conftestpath.check(), conftestpath + try: + return self._conftestpath2mod[conftestpath] + except KeyError: + if not conftestpath.dirpath('__init__.py').check(file=1): + # HACK: we don't want any "globally" imported conftest.py, + # prone to conflicts and subtle problems + modname = str(conftestpath).replace('.', conftestpath.sep) + mod = conftestpath.pyimport(modname=modname) + else: + mod = conftestpath.pyimport() + self._conftestpath2mod[conftestpath] = mod + dirpath = conftestpath.dirpath() + if dirpath in self._path2confmods: + for path, mods in self._path2confmods.items(): + if path and path.relto(dirpath) or path == dirpath: + assert mod not in mods + mods.append(mod) + self._postimport(mod) + return mod + + def _postimport(self, mod): + if self._onimport: + self._onimport(mod) + return mod + + def ensuretemp(string, dir=1): """ (deprecated) return temporary directory path with the given string as the trailing part. It is usually @@ -196,3 +314,5 @@ def onpytestaccess(): # a default per-process instance of py.test configuration config_per_process = Config() + + diff --git a/py/_test/conftesthandle.py b/py/_test/conftesthandle.py deleted file mode 100644 index c496dc233..000000000 --- a/py/_test/conftesthandle.py +++ /dev/null @@ -1,123 +0,0 @@ -import py - -class Conftest(object): - """ the single place for accessing values and interacting - towards conftest modules from py.test objects. - - (deprecated) - Note that triggering Conftest instances to import - conftest.py files may result in added cmdline options. - """ - def __init__(self, onimport=None, confcutdir=None): - self._path2confmods = {} - self._onimport = onimport - self._conftestpath2mod = {} - self._confcutdir = confcutdir - self._md5cache = {} - - def setinitial(self, args): - """ try to find a first anchor path for looking up global values - from conftests. This function is usually called _before_ - argument parsing. conftest files may add command line options - and we thus have no completely safe way of determining - which parts of the arguments are actually related to options - and which are file system paths. We just try here to get - bootstrapped ... - """ - current = py.path.local() - opt = '--confcutdir' - for i in range(len(args)): - opt1 = str(args[i]) - if opt1.startswith(opt): - if opt1 == opt: - if len(args) > i: - p = current.join(args[i+1], abs=True) - elif opt1.startswith(opt + "="): - p = current.join(opt1[len(opt)+1:], abs=1) - self._confcutdir = p - break - for arg in args + [current]: - if hasattr(arg, 'startswith') and arg.startswith("--"): - continue - anchor = current.join(arg, abs=1) - if anchor.check(): # we found some file object - self._path2confmods[None] = self.getconftestmodules(anchor) - # let's also consider test* dirs - if anchor.check(dir=1): - for x in anchor.listdir("test*"): - if x.check(dir=1): - self.getconftestmodules(x) - break - else: - assert 0, "no root of filesystem?" - - def getconftestmodules(self, path): - """ return a list of imported conftest modules for the given path. """ - try: - clist = self._path2confmods[path] - except KeyError: - if path is None: - raise ValueError("missing default confest.") - dp = path.dirpath() - clist = [] - if dp != path: - cutdir = self._confcutdir - if cutdir and path != cutdir and not path.relto(cutdir): - pass - else: - conftestpath = path.join("conftest.py") - if conftestpath.check(file=1): - key = conftestpath.computehash() - # XXX logging about conftest loading - if key not in self._md5cache: - clist.append(self.importconftest(conftestpath)) - self._md5cache[key] = conftestpath - else: - # use some kind of logging - print ("WARN: not loading %s" % conftestpath) - clist[:0] = self.getconftestmodules(dp) - self._path2confmods[path] = clist - # be defensive: avoid changes from caller side to - # affect us by always returning a copy of the actual list - return clist[:] - - def rget(self, name, path=None): - mod, value = self.rget_with_confmod(name, path) - return value - - def rget_with_confmod(self, name, path=None): - modules = self.getconftestmodules(path) - modules.reverse() - for mod in modules: - try: - return mod, getattr(mod, name) - except AttributeError: - continue - raise KeyError(name) - - def importconftest(self, conftestpath): - assert conftestpath.check(), conftestpath - try: - return self._conftestpath2mod[conftestpath] - except KeyError: - if not conftestpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want any "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(conftestpath).replace('.', conftestpath.sep) - mod = conftestpath.pyimport(modname=modname) - else: - mod = conftestpath.pyimport() - self._conftestpath2mod[conftestpath] = mod - dirpath = conftestpath.dirpath() - if dirpath in self._path2confmods: - for path, mods in self._path2confmods.items(): - if path and path.relto(dirpath) or path == dirpath: - assert mod not in mods - mods.append(mod) - self._postimport(mod) - return mod - - def _postimport(self, mod): - if self._onimport: - self._onimport(mod) - return mod diff --git a/testing/test_conftesthandle.py b/testing/test_conftest.py similarity index 99% rename from testing/test_conftesthandle.py rename to testing/test_conftest.py index 7b9c868bb..d30c1c4f0 100644 --- a/testing/test_conftesthandle.py +++ b/testing/test_conftest.py @@ -1,5 +1,5 @@ import py -from py._test.conftesthandle import Conftest +from py._test.config import Conftest def pytest_generate_tests(metafunc): if "basedir" in metafunc.funcargnames: