Hakan Ardo avatar Hakan Ardo committed d532da6 Merge

hg merge default

Comments (0)

Files changed (480)

+.hg
+.svn
+
+*.pyc
+*.pyo
+*~
+
+bin/pypy-c
+include/*.h
+lib_pypy/ctypes_config_cache/_[^_]*_*.py
+pypy/_cache
+pypy/doc/*.html
+pypy/doc/config/*.html
+pypy/doc/discussion/*.html
+pypy/module/test_lib_pypy/ctypes_tests/*.o
+pypy/translator/c/src/dtoa.o
+pypy/translator/goal/pypy-c
+pypy/translator/goal/target*-c
+release/
 ^pypy/module/cpyext/test/.+\.o$
 ^pypy/module/cpyext/test/.+\.obj$
 ^pypy/module/cpyext/test/.+\.manifest$
+^pypy/module/test_lib_pypy/ctypes_tests/.+\.o$
 ^pypy/doc/.+\.html$
 ^pypy/doc/basicblock\.asc$
 ^pypy/doc/.+\.svninfo$
 ^pypy/doc/image/stackless_informal\.png$
 ^pypy/doc/image/parsing_example.+\.png$
 ^compiled
+^.git/
+^release/
 80037 greenlet
 80348 lib_pypy/pyrepl
-80037 testrunner
+80409 testrunner

_pytest/__init__.py

+#

_pytest/assertion.py

+"""
+support for presented detailed information in failing assertions.
+"""
+import py
+import sys
+from _pytest.monkeypatch import monkeypatch
+
+def pytest_addoption(parser):
+    group = parser.getgroup("debugconfig")
+    group._addoption('--no-assert', action="store_true", default=False,
+        dest="noassert",
+        help="disable python assert expression reinterpretation."),
+
+def pytest_configure(config):
+    # The _reprcompare attribute on the py.code module is used by
+    # py._code._assertionnew to detect this plugin was loaded and in
+    # turn call the hooks defined here as part of the
+    # DebugInterpreter.
+    config._monkeypatch = m = monkeypatch()
+    warn_about_missing_assertion()
+    if not config.getvalue("noassert") and not config.getvalue("nomagic"):
+        def callbinrepr(op, left, right):
+            hook_result = config.hook.pytest_assertrepr_compare(
+                config=config, op=op, left=left, right=right)
+            for new_expl in hook_result:
+                if new_expl:
+                    return '\n~'.join(new_expl)
+        m.setattr(py.builtin.builtins,
+                  'AssertionError', py.code._AssertionError)
+        m.setattr(py.code, '_reprcompare', callbinrepr)
+
+def pytest_unconfigure(config):
+    config._monkeypatch.undo()
+
+def warn_about_missing_assertion():
+    try:
+        assert False
+    except AssertionError:
+        pass
+    else:
+        sys.stderr.write("WARNING: failing tests may report as passing because "
+        "assertions are turned off!  (are you using python -O?)\n")
+
+# Provide basestring in python3
+try:
+    basestring = basestring
+except NameError:
+    basestring = str
+
+
+def pytest_assertrepr_compare(op, left, right):
+    """return specialised explanations for some operators/operands"""
+    width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
+    left_repr = py.io.saferepr(left, maxsize=int(width/2))
+    right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
+    summary = '%s %s %s' % (left_repr, op, right_repr)
+
+    issequence = lambda x: isinstance(x, (list, tuple))
+    istext = lambda x: isinstance(x, basestring)
+    isdict = lambda x: isinstance(x, dict)
+    isset = lambda x: isinstance(x, set)
+
+    explanation = None
+    try:
+        if op == '==':
+            if istext(left) and istext(right):
+                explanation = _diff_text(left, right)
+            elif issequence(left) and issequence(right):
+                explanation = _compare_eq_sequence(left, right)
+            elif isset(left) and isset(right):
+                explanation = _compare_eq_set(left, right)
+            elif isdict(left) and isdict(right):
+                explanation = _diff_text(py.std.pprint.pformat(left),
+                                         py.std.pprint.pformat(right))
+        elif op == 'not in':
+            if istext(left) and istext(right):
+                explanation = _notin_text(left, right)
+    except py.builtin._sysex:
+        raise
+    except:
+        excinfo = py.code.ExceptionInfo()
+        explanation = ['(pytest_assertion plugin: representation of '
+            'details failed. Probably an object has a faulty __repr__.)',
+            str(excinfo)
+            ]
+
+
+    if not explanation:
+        return None
+
+    # Don't include pageloads of data, should be configurable
+    if len(''.join(explanation)) > 80*8:
+        explanation = ['Detailed information too verbose, truncated']
+
+    return [summary] + explanation
+
+
+def _diff_text(left, right):
+    """Return the explanation for the diff between text
+
+    This will skip leading and trailing characters which are
+    identical to keep the diff minimal.
+    """
+    explanation = []
+    i = 0 # just in case left or right has zero length
+    for i in range(min(len(left), len(right))):
+        if left[i] != right[i]:
+            break
+    if i > 42:
+        i -= 10                 # Provide some context
+        explanation = ['Skipping %s identical '
+                       'leading characters in diff' % i]
+        left = left[i:]
+        right = right[i:]
+    if len(left) == len(right):
+        for i in range(len(left)):
+            if left[-i] != right[-i]:
+                break
+        if i > 42:
+            i -= 10     # Provide some context
+            explanation += ['Skipping %s identical '
+                            'trailing characters in diff' % i]
+            left = left[:-i]
+            right = right[:-i]
+    explanation += [line.strip('\n')
+                    for line in py.std.difflib.ndiff(left.splitlines(),
+                                                     right.splitlines())]
+    return explanation
+
+
+def _compare_eq_sequence(left, right):
+    explanation = []
+    for i in range(min(len(left), len(right))):
+        if left[i] != right[i]:
+            explanation += ['At index %s diff: %r != %r' %
+                            (i, left[i], right[i])]
+            break
+    if len(left) > len(right):
+        explanation += ['Left contains more items, '
+            'first extra item: %s' % py.io.saferepr(left[len(right)],)]
+    elif len(left) < len(right):
+        explanation += ['Right contains more items, '
+            'first extra item: %s' % py.io.saferepr(right[len(left)],)]
+    return explanation # + _diff_text(py.std.pprint.pformat(left),
+                       #             py.std.pprint.pformat(right))
+
+
+def _compare_eq_set(left, right):
+    explanation = []
+    diff_left = left - right
+    diff_right = right - left
+    if diff_left:
+        explanation.append('Extra items in the left set:')
+        for item in diff_left:
+            explanation.append(py.io.saferepr(item))
+    if diff_right:
+        explanation.append('Extra items in the right set:')
+        for item in diff_right:
+            explanation.append(py.io.saferepr(item))
+    return explanation
+
+
+def _notin_text(term, text):
+    index = text.find(term)
+    head = text[:index]
+    tail = text[index+len(term):]
+    correct_text = head + tail
+    diff = _diff_text(correct_text, text)
+    newdiff = ['%s is contained here:' % py.io.saferepr(term, maxsize=42)]
+    for line in diff:
+        if line.startswith('Skipping'):
+            continue
+        if line.startswith('- '):
+            continue
+        if line.startswith('+ '):
+            newdiff.append('  ' + line[2:])
+        else:
+            newdiff.append(line)
+    return newdiff

_pytest/capture.py

+""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments.  """
+
+import pytest, py
+import os
+
+def pytest_addoption(parser):
+    group = parser.getgroup("general")
+    group._addoption('--capture', action="store", default=None,
+        metavar="method", type="choice", choices=['fd', 'sys', 'no'],
+        help="per-test capturing method: one of fd (default)|sys|no.")
+    group._addoption('-s', action="store_const", const="no", dest="capture",
+        help="shortcut for --capture=no.")
+
+def addouterr(rep, outerr):
+    repr = getattr(rep, 'longrepr', None)
+    if not hasattr(repr, 'addsection'):
+        return
+    for secname, content in zip(["out", "err"], outerr):
+        if content:
+            repr.addsection("Captured std%s" % secname, content.rstrip())
+
+def pytest_unconfigure(config):
+    # registered in config.py during early conftest.py loading
+    capman = config.pluginmanager.getplugin('capturemanager')
+    while capman._method2capture:
+        name, cap = capman._method2capture.popitem()
+        # XXX logging module may wants to close it itself on process exit
+        # otherwise we could do finalization here and call "reset()".
+        cap.suspend()
+
+class NoCapture:
+    def startall(self):
+        pass
+    def resume(self):
+        pass
+    def reset(self):
+        pass
+    def suspend(self):
+        return "", ""
+
+class CaptureManager:
+    def __init__(self):
+        self._method2capture = {}
+
+    def _maketempfile(self):
+        f = py.std.tempfile.TemporaryFile()
+        newf = py.io.dupfile(f, encoding="UTF-8")
+        f.close()
+        return newf
+
+    def _makestringio(self):
+        return py.io.TextIO()
+
+    def _getcapture(self, method):
+        if method == "fd":
+            return py.io.StdCaptureFD(now=False,
+                out=self._maketempfile(), err=self._maketempfile()
+            )
+        elif method == "sys":
+            return py.io.StdCapture(now=False,
+                out=self._makestringio(), err=self._makestringio()
+            )
+        elif method == "no":
+            return NoCapture()
+        else:
+            raise ValueError("unknown capturing method: %r" % method)
+
+    def _getmethod_preoptionparse(self, args):
+        if '-s' in args or "--capture=no" in args:
+            return "no"
+        elif hasattr(os, 'dup') and '--capture=sys' not in args:
+            return "fd"
+        else:
+            return "sys"
+
+    def _getmethod(self, config, fspath):
+        if config.option.capture:
+            method = config.option.capture
+        else:
+            try:
+                method = config._conftest.rget("option_capture", path=fspath)
+            except KeyError:
+                method = "fd"
+        if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
+            method = "sys"
+        return method
+
+    def resumecapture_item(self, item):
+        method = self._getmethod(item.config, item.fspath)
+        if not hasattr(item, 'outerr'):
+            item.outerr = ('', '') # we accumulate outerr on the item
+        return self.resumecapture(method)
+
+    def resumecapture(self, method):
+        if hasattr(self, '_capturing'):
+            raise ValueError("cannot resume, already capturing with %r" %
+                (self._capturing,))
+        cap = self._method2capture.get(method)
+        self._capturing = method
+        if cap is None:
+            self._method2capture[method] = cap = self._getcapture(method)
+            cap.startall()
+        else:
+            cap.resume()
+
+    def suspendcapture(self, item=None):
+        self.deactivate_funcargs()
+        if hasattr(self, '_capturing'):
+            method = self._capturing
+            cap = self._method2capture.get(method)
+            if cap is not None:
+                outerr = cap.suspend()
+            del self._capturing
+            if item:
+                outerr = (item.outerr[0] + outerr[0],
+                          item.outerr[1] + outerr[1])
+            return outerr
+        if hasattr(item, 'outerr'):
+            return item.outerr
+        return "", ""
+
+    def activate_funcargs(self, pyfuncitem):
+        if not hasattr(pyfuncitem, 'funcargs'):
+            return
+        assert not hasattr(self, '_capturing_funcargs')
+        self._capturing_funcargs = capturing_funcargs = []
+        for name, capfuncarg in pyfuncitem.funcargs.items():
+            if name in ('capsys', 'capfd'):
+                capturing_funcargs.append(capfuncarg)
+                capfuncarg._start()
+
+    def deactivate_funcargs(self):
+        capturing_funcargs = getattr(self, '_capturing_funcargs', None)
+        if capturing_funcargs is not None:
+            while capturing_funcargs:
+                capfuncarg = capturing_funcargs.pop()
+                capfuncarg._finalize()
+            del self._capturing_funcargs
+
+    def pytest_make_collect_report(self, __multicall__, collector):
+        method = self._getmethod(collector.config, collector.fspath)
+        try:
+            self.resumecapture(method)
+        except ValueError:
+            return # recursive collect, XXX refactor capturing
+                   # to allow for more lightweight recursive capturing
+        try:
+            rep = __multicall__.execute()
+        finally:
+            outerr = self.suspendcapture()
+        addouterr(rep, outerr)
+        return rep
+
+    @pytest.mark.tryfirst
+    def pytest_runtest_setup(self, item):
+        self.resumecapture_item(item)
+
+    @pytest.mark.tryfirst
+    def pytest_runtest_call(self, item):
+        self.resumecapture_item(item)
+        self.activate_funcargs(item)
+
+    @pytest.mark.tryfirst
+    def pytest_runtest_teardown(self, item):
+        self.resumecapture_item(item)
+
+    def pytest__teardown_final(self, __multicall__, session):
+        method = self._getmethod(session.config, None)
+        self.resumecapture(method)
+        try:
+            rep = __multicall__.execute()
+        finally:
+            outerr = self.suspendcapture()
+        if rep:
+            addouterr(rep, outerr)
+        return rep
+
+    def pytest_keyboard_interrupt(self, excinfo):
+        if hasattr(self, '_capturing'):
+            self.suspendcapture()
+
+    @pytest.mark.tryfirst
+    def pytest_runtest_makereport(self, __multicall__, item, call):
+        self.deactivate_funcargs()
+        rep = __multicall__.execute()
+        outerr = self.suspendcapture(item)
+        if not rep.passed:
+            addouterr(rep, outerr)
+        if not rep.passed or rep.when == "teardown":
+            outerr = ('', '')
+        item.outerr = outerr
+        return rep
+
+def pytest_funcarg__capsys(request):
+    """enables capturing of writes to sys.stdout/sys.stderr and makes
+    captured output available via ``capsys.readouterr()`` method calls
+    which return a ``(out, err)`` tuple.
+    """
+    return CaptureFuncarg(py.io.StdCapture)
+
+def pytest_funcarg__capfd(request):
+    """enables capturing of writes to file descriptors 1 and 2 and makes
+    captured output available via ``capsys.readouterr()`` method calls
+    which return a ``(out, err)`` tuple.
+    """
+    if not hasattr(os, 'dup'):
+        py.test.skip("capfd funcarg needs os.dup")
+    return CaptureFuncarg(py.io.StdCaptureFD)
+
+class CaptureFuncarg:
+    def __init__(self, captureclass):
+        self.capture = captureclass(now=False)
+
+    def _start(self):
+        self.capture.startall()
+
+    def _finalize(self):
+        if hasattr(self, 'capture'):
+            self.capture.reset()
+            del self.capture
+
+    def readouterr(self):
+        return self.capture.readouterr()
+
+    def close(self):
+        self._finalize()

_pytest/config.py

+""" command line options, ini-file and conftest.py processing. """
+
+import py
+import sys, os
+from _pytest.core import PluginManager
+import pytest
+
+def pytest_cmdline_parse(pluginmanager, args):
+    config = Config(pluginmanager)
+    config.parse(args)
+    if config.option.debug:
+        config.trace.root.setwriter(sys.stderr.write)
+    return config
+
+class Parser:
+    """ Parser for command line arguments. """
+
+    def __init__(self, usage=None, processopt=None):
+        self._anonymous = OptionGroup("custom options", parser=self)
+        self._groups = []
+        self._processopt = processopt
+        self._usage = usage
+        self._inidict = {}
+        self._ininames = []
+        self.hints = []
+
+    def processoption(self, option):
+        if self._processopt:
+            if option.dest:
+                self._processopt(option)
+
+    def addnote(self, note):
+        self._notes.append(note)
+
+    def getgroup(self, name, description="", after=None):
+        """ get (or create) a named option Group.
+
+        :name: unique name of the option group.
+        :description: long description for --help output.
+        :after: name of other group, used for ordering --help output.
+        """
+        for group in self._groups:
+            if group.name == name:
+                return group
+        group = OptionGroup(name, description, parser=self)
+        i = 0
+        for i, grp in enumerate(self._groups):
+            if grp.name == after:
+                break
+        self._groups.insert(i+1, group)
+        return group
+
+    def addoption(self, *opts, **attrs):
+        """ add an optparse-style option. """
+        self._anonymous.addoption(*opts, **attrs)
+
+    def parse(self, args):
+        self.optparser = optparser = MyOptionParser(self)
+        groups = self._groups + [self._anonymous]
+        for group in groups:
+            if group.options:
+                desc = group.description or group.name
+                optgroup = py.std.optparse.OptionGroup(optparser, desc)
+                optgroup.add_options(group.options)
+                optparser.add_option_group(optgroup)
+        return self.optparser.parse_args([str(x) for x in args])
+
+    def parse_setoption(self, args, option):
+        parsedoption, args = self.parse(args)
+        for name, value in parsedoption.__dict__.items():
+            setattr(option, name, value)
+        return args
+
+    def addini(self, name, help, type=None, default=None):
+        """ add an ini-file option with the given name and description. """
+        assert type in (None, "pathlist", "args", "linelist")
+        self._inidict[name] = (help, type, default)
+        self._ininames.append(name)
+
+class OptionGroup:
+    def __init__(self, name, description="", parser=None):
+        self.name = name
+        self.description = description
+        self.options = []
+        self.parser = parser
+
+    def addoption(self, *optnames, **attrs):
+        """ add an option to this group. """
+        option = py.std.optparse.Option(*optnames, **attrs)
+        self._addoption_instance(option, shortupper=False)
+
+    def _addoption(self, *optnames, **attrs):
+        option = py.std.optparse.Option(*optnames, **attrs)
+        self._addoption_instance(option, shortupper=True)
+
+    def _addoption_instance(self, option, shortupper=False):
+        if not shortupper:
+            for opt in option._short_opts:
+                if opt[0] == '-' and opt[1].islower():
+                    raise ValueError("lowercase shortoptions reserved")
+        if self.parser:
+            self.parser.processoption(option)
+        self.options.append(option)
+
+
+class MyOptionParser(py.std.optparse.OptionParser):
+    def __init__(self, parser):
+        self._parser = parser
+        py.std.optparse.OptionParser.__init__(self, usage=parser._usage,
+            add_help_option=False)
+    def format_epilog(self, formatter):
+        hints = self._parser.hints
+        if hints:
+            s = "\n".join(["hint: " + x for x in hints]) + "\n"
+            s = "\n" + s + "\n"
+            return s
+        return ""
+
+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
+
+    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):
+                        clist.append(self.importconftest(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:
+            pkgpath = conftestpath.pypkgpath()
+            if pkgpath is None:
+                _ensure_removed_sysmodule(conftestpath.purebasename)
+            self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
+            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 _ensure_removed_sysmodule(modname):
+    try:
+        del sys.modules[modname]
+    except KeyError:
+        pass
+
+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 Config(object):
+    """ access to configuration values, pluginmanager and plugin hooks.  """
+    def __init__(self, pluginmanager=None):
+        #: command line option values, usually added via parser.addoption(...)
+        #: or parser.getgroup(...).addoption(...) calls
+        self.option = CmdOptions()
+        self._parser = Parser(
+            usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
+            processopt=self._processopt,
+        )
+        #: a pluginmanager instance
+        self.pluginmanager = pluginmanager or PluginManager(load=True)
+        self.trace = self.pluginmanager.trace.root.get("config")
+        self._conftest = Conftest(onimport=self._onimportconftest)
+        self.hook = self.pluginmanager.hook
+        self._inicache = {}
+
+    def _onimportconftest(self, conftestmodule):
+        self.trace("loaded conftestmodule %r" %(conftestmodule,))
+        self.pluginmanager.consider_conftest(conftestmodule)
+
+    def _processopt(self, opt):
+        if hasattr(opt, 'default') and opt.dest:
+            if not hasattr(self.option, opt.dest):
+                setattr(self.option, opt.dest, opt.default)
+
+    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 _setinitialconftest(self, args):
+        # capture output during conftest init (#issue93)
+        from _pytest.capture import CaptureManager
+        capman = CaptureManager()
+        self.pluginmanager.register(capman, 'capturemanager')
+        # will be unregistered in capture.py's unconfigure()
+        capman.resumecapture(capman._getmethod_preoptionparse(args))
+        try:
+            try:
+                self._conftest.setinitial(args)
+            finally:
+                out, err = capman.suspendcapture() # logging might have got it
+        except:
+            sys.stdout.write(out)
+            sys.stderr.write(err)
+            raise
+
+    def _initini(self, args):
+        self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
+        self._parser.addini('addopts', 'extra command line options', 'args')
+        self._parser.addini('minversion', 'minimally required pytest version')
+
+    def _preparse(self, args, addopts=True):
+        self._initini(args)
+        if addopts:
+            args[:] = self.getini("addopts") + args
+        self._checkversion()
+        self.pluginmanager.consider_preparse(args)
+        self.pluginmanager.consider_setuptools_entrypoints()
+        self.pluginmanager.consider_env()
+        self._setinitialconftest(args)
+        self.pluginmanager.do_addoption(self._parser)
+        if addopts:
+            self.hook.pytest_cmdline_preparse(config=self, args=args)
+
+    def _checkversion(self):
+        minver = self.inicfg.get('minversion', None)
+        if minver:
+            ver = minver.split(".")
+            myver = pytest.__version__.split(".")
+            if myver < ver:
+                raise pytest.UsageError(
+                    "%s:%d: requires pytest-%s, actual pytest-%s'" %(
+                    self.inicfg.config.path, self.inicfg.lineof('minversion'),
+                    minver, pytest.__version__))
+
+    def parse(self, args):
+        # parse given 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.args = args
+
+    def getini(self, name):
+        """ return configuration value from an ini file. If the
+        specified name hasn't been registered through a prior ``parse.addini``
+        call (usually from a plugin), a ValueError is raised. """
+        try:
+            return self._inicache[name]
+        except KeyError:
+            self._inicache[name] = val = self._getini(name)
+            return val
+
+    def _getini(self, name):
+        try:
+            description, type, default = self._parser._inidict[name]
+        except KeyError:
+            raise ValueError("unknown configuration value: %r" %(name,))
+        try:
+            value = self.inicfg[name]
+        except KeyError:
+            if default is not None:
+                return default
+            if type is None:
+                return ''
+            return []
+        if type == "pathlist":
+            dp = py.path.local(self.inicfg.config.path).dirpath()
+            l = []
+            for relpath in py.std.shlex.split(value):
+                l.append(dp.join(relpath, abs=True))
+            return l
+        elif type == "args":
+            return py.std.shlex.split(value)
+        elif type == "linelist":
+            return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
+        else:
+            assert type is None
+            return value
+
+    def _getconftest_pathlist(self, name, path=None):
+        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 _getconftest(self, name, path=None, check=False):
+        if check:
+            self._checkconftest(name)
+        return self._conftest.rget(name, path)
+
+    def getvalue(self, name, path=None):
+        """ return ``name`` value looked set from command line options.
+
+        (deprecated) if we can't find the option also lookup
+        the name in a matching conftest file.
+        """
+        try:
+            return getattr(self.option, name)
+        except AttributeError:
+            return self._getconftest(name, path, check=False)
+
+    def getvalueorskip(self, name, path=None):
+        """ (deprecated) return getvalue(name) or call
+        py.test.skip if no value exists. """
+        __tracebackhide__ = True
+        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 getcfg(args, inibasenames):
+    args = [x for x in args if str(x)[0] != "-"]
+    if not args:
+        args = [py.path.local()]
+    for arg in args:
+        arg = py.path.local(arg)
+        for base in arg.parts(reverse=True):
+            for inibasename in inibasenames:
+                p = base.join(inibasename)
+                if p.check():
+                    iniconfig = py.iniconfig.IniConfig(p)
+                    if 'pytest' in iniconfig.sections:
+                        return iniconfig['pytest']
+    return {}
+
+def findupwards(current, basename):
+    current = py.path.local(current)
+    while 1:
+        p = current.join(basename)
+        if p.check():
+            return p
+        p = current.dirpath()
+        if p == current:
+            return
+        current = p
+
+"""
+pytest PluginManager, basic initialization and tracing.
+(c) Holger Krekel 2004-2010
+"""
+import sys, os
+import inspect
+import py
+from _pytest import hookspec # the extension point definitions
+
+assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
+    "%s is too old, remove or upgrade 'py'" % (py.__version__))
+
+default_plugins = (
+ "config mark main terminal runner python pdb unittest capture skipping "
+ "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
+ "junitxml resultlog doctest").split()
+
+class TagTracer:
+    def __init__(self, prefix="[pytest] "):
+        self._tag2proc = {}
+        self.writer = None
+        self.indent = 0
+        self.prefix = prefix
+
+    def get(self, name):
+        return TagTracerSub(self, (name,))
+
+    def processmessage(self, tags, args):
+        if self.writer is not None:
+            if args:
+                indent = "  " * self.indent
+                content = " ".join(map(str, args))
+                self.writer("%s%s%s\n" %(self.prefix, indent, content))
+        try:
+            self._tag2proc[tags](tags, args)
+        except KeyError:
+            pass
+
+    def setwriter(self, writer):
+        self.writer = writer
+
+    def setprocessor(self, tags, processor):
+        if isinstance(tags, str):
+            tags = tuple(tags.split(":"))
+        else:
+            assert isinstance(tags, tuple)
+        self._tag2proc[tags] = processor
+
+class TagTracerSub:
+    def __init__(self, root, tags):
+        self.root = root
+        self.tags = tags
+    def __call__(self, *args):
+        self.root.processmessage(self.tags, args)
+    def setmyprocessor(self, processor):
+        self.root.setprocessor(self.tags, processor)
+    def get(self, name):
+        return self.__class__(self.root, self.tags + (name,))
+
+class PluginManager(object):
+    def __init__(self, load=False):
+        self._name2plugin = {}
+        self._listattrcache = {}
+        self._plugins = []
+        self._hints = []
+        self.trace = TagTracer().get("pluginmanage")
+        self._plugin_distinfo = []
+        if os.environ.get('PYTEST_DEBUG'):
+            err = sys.stderr
+            encoding = getattr(err, 'encoding', 'utf8')
+            try:
+                err = py.io.dupfile(err, encoding=encoding)
+            except Exception:
+                pass
+            self.trace.root.setwriter(err.write)
+        self.hook = HookRelay([hookspec], pm=self)
+        self.register(self)
+        if load:
+            for spec in default_plugins:
+                self.import_plugin(spec)
+
+    def register(self, plugin, name=None, prepend=False):
+        assert not self.isregistered(plugin), plugin
+        name = name or getattr(plugin, '__name__', str(id(plugin)))
+        if name in self._name2plugin:
+            return False
+        #self.trace("registering", name, plugin)
+        self._name2plugin[name] = plugin
+        self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
+        self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
+        if not prepend:
+            self._plugins.append(plugin)
+        else:
+            self._plugins.insert(0, plugin)
+        return True
+
+    def unregister(self, plugin=None, name=None):
+        if plugin is None:
+            plugin = self.getplugin(name=name)
+        self._plugins.remove(plugin)
+        self.hook.pytest_plugin_unregistered(plugin=plugin)
+        for name, value in list(self._name2plugin.items()):
+            if value == plugin:
+                del self._name2plugin[name]
+
+    def isregistered(self, plugin, name=None):
+        if self.getplugin(name) is not None:
+            return True
+        for val in self._name2plugin.values():
+            if plugin == val:
+                return True
+
+    def addhooks(self, spec):
+        self.hook._addhooks(spec, prefix="pytest_")
+
+    def getplugins(self):
+        return list(self._plugins)
+
+    def skipifmissing(self, name):
+        if not self.hasplugin(name):
+            py.test.skip("plugin %r is missing" % name)
+
+    def hasplugin(self, name):
+        return bool(self.getplugin(name))
+
+    def getplugin(self, name):
+        if name is None:
+            return None
+        try:
+            return self._name2plugin[name]
+        except KeyError:
+            return self._name2plugin.get("_pytest." + name, None)
+
+    # API for bootstrapping
+    #
+    def _envlist(self, varname):
+        val = py.std.os.environ.get(varname, None)
+        if val is not None:
+            return val.split(',')
+        return ()
+
+    def consider_env(self):
+        for spec in self._envlist("PYTEST_PLUGINS"):
+            self.import_plugin(spec)
+
+    def consider_setuptools_entrypoints(self):
+        try:
+            from pkg_resources import iter_entry_points, DistributionNotFound
+        except ImportError:
+            return # XXX issue a warning
+        for ep in iter_entry_points('pytest11'):
+            name = ep.name
+            if name.startswith("pytest_"):
+                name = name[7:]
+            if ep.name in self._name2plugin or name in self._name2plugin:
+                continue
+            try:
+                plugin = ep.load()
+            except DistributionNotFound:
+                continue
+            self._plugin_distinfo.append((ep.dist, plugin))
+            self.register(plugin, name=name)
+
+    def consider_preparse(self, args):
+        for opt1,opt2 in zip(args, args[1:]):
+            if opt1 == "-p":
+                if opt2.startswith("no:"):
+                    name = opt2[3:]
+                    if self.getplugin(name) is not None:
+                        self.unregister(None, name=name)
+                    self._name2plugin[name] = -1
+                else:
+                    if self.getplugin(opt2) is None:
+                        self.import_plugin(opt2)
+
+    def consider_conftest(self, conftestmodule):
+        if self.register(conftestmodule, name=conftestmodule.__file__):
+            self.consider_module(conftestmodule)
+
+    def consider_module(self, mod):
+        attr = getattr(mod, "pytest_plugins", ())
+        if attr:
+            if not isinstance(attr, (list, tuple)):
+                attr = (attr,)
+            for spec in attr:
+                self.import_plugin(spec)
+
+    def import_plugin(self, modname):
+        assert isinstance(modname, str)
+        if self.getplugin(modname) is not None:
+            return
+        try:
+            #self.trace("importing", modname)
+            mod = importplugin(modname)
+        except KeyboardInterrupt:
+            raise
+        except ImportError:
+            if modname.startswith("pytest_"):
+                return self.import_plugin(modname[7:])
+            raise
+        except:
+            e = py.std.sys.exc_info()[1]
+            if not hasattr(py.test, 'skip'):
+                raise
+            elif not isinstance(e, py.test.skip.Exception):
+                raise
+            self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
+        else:
+            self.register(mod, modname)
+            self.consider_module(mod)
+
+    def pytest_plugin_registered(self, plugin):
+        import pytest
+        dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
+        if dic:
+            self._setns(pytest, dic)
+        if hasattr(self, '_config'):
+            self.call_plugin(plugin, "pytest_addoption",
+                {'parser': self._config._parser})
+            self.call_plugin(plugin, "pytest_configure",
+                {'config': self._config})
+
+    def _setns(self, obj, dic):
+        import pytest
+        for name, value in dic.items():
+            if isinstance(value, dict):
+                mod = getattr(obj, name, None)
+                if mod is None:
+                    modname = "pytest.%s" % name
+                    mod = py.std.types.ModuleType(modname)
+                    sys.modules[modname] = mod
+                    mod.__all__ = []
+                    setattr(obj, name, mod)
+                obj.__all__.append(name)
+                self._setns(mod, value)
+            else:
+                setattr(obj, name, value)
+                obj.__all__.append(name)
+                #if obj != pytest:
+                #    pytest.__all__.append(name)
+                setattr(pytest, name, value)
+
+    def pytest_terminal_summary(self, terminalreporter):
+        tw = terminalreporter._tw
+        if terminalreporter.config.option.traceconfig:
+            for hint in self._hints:
+                tw.line("hint: %s" % hint)
+
+    def do_addoption(self, parser):
+        mname = "pytest_addoption"
+        methods = reversed(self.listattr(mname))
+        MultiCall(methods, {'parser': parser}).execute()
+
+    def do_configure(self, config):
+        assert not hasattr(self, '_config')
+        self._config = config
+        config.hook.pytest_configure(config=self._config)
+
+    def do_unconfigure(self, config):
+        config = self._config
+        del self._config
+        config.hook.pytest_unconfigure(config=config)
+        config.pluginmanager.unregister(self)
+
+    def notify_exception(self, excinfo):
+        excrepr = excinfo.getrepr(funcargs=True, showlocals=True)
+        res = self.hook.pytest_internalerror(excrepr=excrepr)
+        if not py.builtin.any(res):
+            for line in str(excrepr).split("\n"):
+                sys.stderr.write("INTERNALERROR> %s\n" %line)
+                sys.stderr.flush()
+
+    def listattr(self, attrname, plugins=None):
+        if plugins is None:
+            plugins = self._plugins
+        key = (attrname,) + tuple(plugins)
+        try:
+            return list(self._listattrcache[key])
+        except KeyError:
+            pass
+        l = []
+        last = []
+        for plugin in plugins:
+            try:
+                meth = getattr(plugin, attrname)
+                if hasattr(meth, 'tryfirst'):
+                    last.append(meth)
+                elif hasattr(meth, 'trylast'):
+                    l.insert(0, meth)
+                else:
+                    l.append(meth)
+            except AttributeError:
+                continue
+        l.extend(last)
+        self._listattrcache[key] = list(l)
+        return l
+
+    def call_plugin(self, plugin, methname, kwargs):
+        return MultiCall(methods=self.listattr(methname, plugins=[plugin]),
+                kwargs=kwargs, firstresult=True).execute()
+
+
+def importplugin(importspec):
+    name = importspec
+    try:
+        mod = "_pytest." + name
+        return __import__(mod, None, None, '__doc__')
+    except ImportError:
+        #e = py.std.sys.exc_info()[1]
+        #if str(e).find(name) == -1:
+        #    raise
+        pass #
+    return __import__(importspec, None, None, '__doc__')
+
+class MultiCall:
+    """ execute a call into multiple python functions/methods. """
+    def __init__(self, methods, kwargs, firstresult=False):
+        self.methods = list(methods)
+        self.kwargs = kwargs
+        self.results = []
+        self.firstresult = firstresult
+
+    def __repr__(self):
+        status = "%d results, %d meths" % (len(self.results), len(self.methods))
+        return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
+
+    def execute(self):
+        while self.methods:
+            method = self.methods.pop()
+            kwargs = self.getkwargs(method)
+            res = method(**kwargs)
+            if res is not None:
+                self.results.append(res)
+                if self.firstresult:
+                    return res
+        if not self.firstresult:
+            return self.results
+
+    def getkwargs(self, method):
+        kwargs = {}
+        for argname in varnames(method):
+            try:
+                kwargs[argname] = self.kwargs[argname]
+            except KeyError:
+                if argname == "__multicall__":
+                    kwargs[argname] = self
+        return kwargs
+
+def varnames(func):
+    try:
+        return func._varnames
+    except AttributeError:
+        pass
+    if not inspect.isfunction(func) and not inspect.ismethod(func):
+        func = getattr(func, '__call__', func)
+    ismethod = inspect.ismethod(func)
+    rawcode = py.code.getrawcode(func)
+    try:
+        x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
+    except AttributeError:
+        x = ()
+    py.builtin._getfuncdict(func)['_varnames'] = x
+    return x
+
+class HookRelay:
+    def __init__(self, hookspecs, pm, prefix="pytest_"):
+        if not isinstance(hookspecs, list):
+            hookspecs = [hookspecs]
+        self._hookspecs = []
+        self._pm = pm
+        self.trace = pm.trace.root.get("hook")
+        for hookspec in hookspecs:
+            self._addhooks(hookspec, prefix)
+
+    def _addhooks(self, hookspecs, prefix):
+        self._hookspecs.append(hookspecs)
+        added = False
+        for name, method in vars(hookspecs).items():
+            if name.startswith(prefix):
+                firstresult = getattr(method, 'firstresult', False)
+                hc = HookCaller(self, name, firstresult=firstresult)
+                setattr(self, name, hc)
+                added = True
+                #print ("setting new hook", name)
+        if not added:
+            raise ValueError("did not find new %r hooks in %r" %(
+                prefix, hookspecs,))
+
+
+class HookCaller:
+    def __init__(self, hookrelay, name, firstresult):
+        self.hookrelay = hookrelay
+        self.name = name
+        self.firstresult = firstresult
+        self.trace = self.hookrelay.trace
+
+    def __repr__(self):
+        return "<HookCaller %r>" %(self.name,)
+
+    def __call__(self, **kwargs):
+        methods = self.hookrelay._pm.listattr(self.name)
+        return self._docall(methods, kwargs)
+
+    def pcall(self, plugins, **kwargs):
+        methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
+        return self._docall(methods, kwargs)
+
+    def _docall(self, methods, kwargs):
+        self.trace(self.name, kwargs)
+        self.trace.root.indent += 1
+        mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
+        try:
+            res = mc.execute()
+            if res:
+                self.trace("finish", self.name, "-->", res)
+        finally:
+            self.trace.root.indent -= 1
+        return res
+
+_preinit = []
+
+def _preloadplugins():
+    _preinit.append(PluginManager(load=True))
+
+def main(args=None, plugins=None):
+    """ returned exit code integer, after an in-process testing run
+    with the given command line arguments, preloading an optional list
+    of passed in plugin objects. """
+    if args is None:
+        args = sys.argv[1:]
+    elif isinstance(args, py.path.local):
+        args = [str(args)]
+    elif not isinstance(args, (tuple, list)):
+        if not isinstance(args, str):
+            raise ValueError("not a string or argument list: %r" % (args,))
+        args = py.std.shlex.split(args)
+    if _preinit:
+       _pluginmanager = _preinit.pop(0)
+    else: # subsequent calls to main will create a fresh instance
+        _pluginmanager = PluginManager(load=True)
+    hook = _pluginmanager.hook
+    try:
+        if plugins:
+            for plugin in plugins:
+                _pluginmanager.register(plugin)
+        config = hook.pytest_cmdline_parse(
+                pluginmanager=_pluginmanager, args=args)
+        exitstatus = hook.pytest_cmdline_main(config=config)
+    except UsageError:
+        e = sys.exc_info()[1]
+        sys.stderr.write("ERROR: %s\n" %(e.args[0],))
+        exitstatus = 3
+    return exitstatus
+
+class UsageError(Exception):
+    """ error in py.test usage or invocation"""
+

_pytest/doctest.py

+""" discover and run doctests in modules and test files."""
+
+import pytest, py
+from py._code.code import TerminalRepr, ReprFileLocation
+
+def pytest_addoption(parser):
+    group = parser.getgroup("collect")
+    group.addoption("--doctest-modules",
+        action="store_true", default=False,
+        help="run doctests in all .py modules",
+        dest="doctestmodules")
+    group.addoption("--doctest-glob",
+        action="store", default="test*.txt", metavar="pat",
+        help="doctests file matching pattern, default: test*.txt",
+        dest="doctestglob")
+
+def pytest_collect_file(path, parent):
+    config = parent.config
+    if path.ext == ".py":
+        if config.option.doctestmodules:
+            return DoctestModule(path, parent)
+    elif (path.ext in ('.txt', '.rst') and parent.session.isinitpath(path)) or \
+        path.check(fnmatch=config.getvalue("doctestglob")):
+        return DoctestTextfile(path, parent)
+
+class ReprFailDoctest(TerminalRepr):
+    def __init__(self, reprlocation, lines):
+        self.reprlocation = reprlocation
+        self.lines = lines
+    def toterminal(self, tw):
+        for line in self.lines:
+            tw.line(line)
+        self.reprlocation.toterminal(tw)
+
+class DoctestItem(pytest.Item):
+    def repr_failure(self, excinfo):
+        doctest = py.std.doctest
+        if excinfo.errisinstance((doctest.DocTestFailure,
+                                  doctest.UnexpectedException)):
+            doctestfailure = excinfo.value
+            example = doctestfailure.example
+            test = doctestfailure.test
+            filename = test.filename
+            lineno = test.lineno + example.lineno + 1
+            message = excinfo.type.__name__
+            reprlocation = ReprFileLocation(filename, lineno, message)
+            checker = py.std.doctest.OutputChecker()
+            REPORT_UDIFF = py.std.doctest.REPORT_UDIFF
+            filelines = py.path.local(filename).readlines(cr=0)
+            i = max(test.lineno, max(0, lineno - 10)) # XXX?
+            lines = []
+            for line in filelines[i:lineno]:
+                lines.append("%03d %s" % (i+1, line))
+                i += 1
+            if excinfo.errisinstance(doctest.DocTestFailure):
+                lines += checker.output_difference(example,
+                        doctestfailure.got, REPORT_UDIFF).split("\n")
+            else:
+                inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
+                lines += ["UNEXPECTED EXCEPTION: %s" %
+                            repr(inner_excinfo.value)]
+
+            return ReprFailDoctest(reprlocation, lines)
+        else:
+            return super(DoctestItem, self).repr_failure(excinfo)
+
+    def reportinfo(self):
+        return self.fspath, None, "[doctest]"
+
+class DoctestTextfile(DoctestItem, pytest.File):
+    def runtest(self):
+        doctest = py.std.doctest
+        failed, tot = doctest.testfile(
+            str(self.fspath), module_relative=False,
+            optionflags=doctest.ELLIPSIS,
+            raise_on_error=True, verbose=0)
+
+class DoctestModule(DoctestItem, pytest.File):
+    def runtest(self):
+        doctest = py.std.doctest
+        if self.fspath.basename == "conftest.py":
+            module = self.config._conftest.importconftest(self.fspath)
+        else:
+            module = self.fspath.pyimport()
+        failed, tot = doctest.testmod(
+            module, raise_on_error=True, verbose=0,
+            optionflags=doctest.ELLIPSIS)

_pytest/genscript.py

+""" generate a single-file self-contained version of py.test """
+import py
+
+def find_toplevel(name):
+    for syspath in py.std.sys.path:
+        base = py.path.local(syspath)
+        lib = base/name
+        if lib.check(dir=1):
+            return lib
+        mod = base.join("%s.py" % name)
+        if mod.check(file=1):
+            return mod
+    raise LookupError(name)
+
+def pkgname(toplevel, rootpath, path):
+    parts = path.parts()[len(rootpath.parts()):]
+    return '.'.join([toplevel] + [x.purebasename for x in parts])
+
+def pkg_to_mapping(name):
+    toplevel = find_toplevel(name)
+    name2src = {}
+    if toplevel.check(file=1): # module
+        name2src[toplevel.purebasename] = toplevel.read()
+    else: # package
+        for pyfile in toplevel.visit('*.py'):
+            pkg = pkgname(name, toplevel, pyfile)
+            name2src[pkg] = pyfile.read()
+    return name2src
+
+def compress_mapping(mapping):
+    data = py.std.pickle.dumps(mapping, 2)
+    data = py.std.zlib.compress(data, 9)
+    data = py.std.base64.encodestring(data)
+    data = data.decode('ascii')
+    return data
+
+
+def compress_packages(names):
+    mapping = {}
+    for name in names:
+        mapping.update(pkg_to_mapping(name))
+    return compress_mapping(mapping)
+
+def generate_script(entry, packages):
+    data = compress_packages(packages)
+    tmpl = py.path.local(__file__).dirpath().join('standalonetemplate.py')
+    exe = tmpl.read()
+    exe = exe.replace('@SOURCES@', data)
+    exe = exe.replace('@ENTRY@', entry)
+    return exe
+
+
+def pytest_addoption(parser):
+    group = parser.getgroup("debugconfig")
+    group.addoption("--genscript", action="store", default=None,
+        dest="genscript", metavar="path",
+        help="create standalone py.test script at given target path.")
+
+def pytest_cmdline_main(config):
+    genscript = config.getvalue("genscript")
+    if genscript:
+        script = generate_script(
+            'import py; raise SystemExit(py.test.cmdline.main())',
+            ['py', '_pytest', 'pytest'],
+        )
+
+        genscript = py.path.local(genscript)
+        genscript.write(script)
+        return 0

_pytest/helpconfig.py

+""" version info, help messages, tracing configuration.  """
+import py
+import pytest
+import inspect, sys
+from _pytest.core import varnames
+
+def pytest_addoption(parser):
+    group = parser.getgroup('debugconfig')
+    group.addoption('--version', action="store_true",
+            help="display pytest lib version and import information.")
+    group._addoption("-h", "--help", action="store_true", dest="help",
+            help="show help message and configuration info")
+    group._addoption('-p', action="append", dest="plugins", default = [],
+               metavar="name",
+               help="early-load given plugin (multi-allowed).")
+    group.addoption('--traceconfig',
+               action="store_true", dest="traceconfig", default=False,
+               help="trace considerations of conftest.py files."),
+    group._addoption('--nomagic',
+               action="store_true", dest="nomagic", default=False,
+               help="don't reinterpret asserts, no traceback cutting. ")
+    group.addoption('--debug',
+               action="store_true", dest="debug", default=False,
+               help="generate and show internal debugging information.")
+
+
+def pytest_cmdline_main(config):
+    if config.option.version:
+        p = py.path.local(pytest.__file__)
+        sys.stderr.write("This is py.test version %s, imported from %s\n" %
+            (pytest.__version__, p))
+        plugininfo = getpluginversioninfo(config)
+        if plugininfo:
+            for line in plugininfo:
+                sys.stderr.write(line + "\n")
+        return 0
+    elif config.option.help:
+        config.pluginmanager.do_configure(config)
+        showhelp(config)
+        return 0
+
+def showhelp(config):
+    tw = py.io.TerminalWriter()
+    tw.write(config._parser.optparser.format_help())
+    tw.line()
+    tw.line()
+    #tw.sep( "=", "config file settings")
+    tw.line("[pytest] ini-options in the next "
+            "pytest.ini|tox.ini|setup.cfg file:")
+    tw.line()
+
+    for name in config._parser._ininames:
+        help, type, default = config._parser._inidict[name]
+        if type is None:
+            type = "string"
+        spec = "%s (%s)" % (name, type)
+        line = "  %-24s %s" %(spec, help)
+        tw.line(line[:tw.fullwidth])
+
+    tw.line() ; tw.line()