[Pytest-commit] commit/pytest: 10 new changesets

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed May 6 15:02:13 CEST 2015


10 new commits in pytest:

https://bitbucket.org/pytest-dev/pytest/commits/8d6251cf5ac6/
Changeset:   8d6251cf5ac6
Branch:      pluggy1
User:        hpk42
Date:        2015-04-29 14:40:51+00:00
Summary:     adapt pytest to using pluggy (current master)
Affected #:  10 files

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -9,7 +9,7 @@
 # DON't import pytest here because it causes import cycle troubles
 import sys, os
 from _pytest import hookspec # the extension point definitions
-from _pytest.core import PluginManager, hookimpl_opts, varnames
+from pluggy import PluginManager, hookimpl_opts, varnames
 
 # pytest startup
 #

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/core.py
--- a/_pytest/core.py
+++ /dev/null
@@ -1,590 +0,0 @@
-"""
-PluginManager, basic initialization and tracing.
-"""
-import sys
-from inspect import isfunction, ismethod, isclass, formatargspec, getargspec
-import py
-
-py3 = sys.version_info > (3,0)
-
-def hookspec_opts(firstresult=False, historic=False):
-    """ returns a decorator which will define a function as a hook specfication.
-
-    If firstresult is True the 1:N hook call (N being the number of registered
-    hook implementation functions) will stop at I<=N when the I'th function
-    returns a non-None result.
-
-    If historic is True calls to a hook will be memorized and replayed
-    on later registered plugins.
-    """
-    def setattr_hookspec_opts(func):
-        if historic and firstresult:
-            raise ValueError("cannot have a historic firstresult hook")
-        if firstresult:
-            func.firstresult = firstresult
-        if historic:
-            func.historic = historic
-        return func
-    return setattr_hookspec_opts
-
-
-def hookimpl_opts(hookwrapper=False, optionalhook=False,
-                  tryfirst=False, trylast=False):
-    """ Return a decorator which marks a function as a hook implementation.
-
-    If optionalhook is True a missing matching hook specification will not result
-    in an error (by default it is an error if no matching spec is found).
-
-    If tryfirst is True this hook implementation will run as early as possible
-    in the chain of N hook implementations for a specfication.
-
-    If trylast is True this hook implementation will run as late as possible
-    in the chain of N hook implementations.
-
-    If hookwrapper is True the hook implementations needs to execute exactly
-    one "yield".  The code before the yield is run early before any non-hookwrapper
-    function is run.  The code after the yield is run after all non-hookwrapper
-    function have run.  The yield receives an ``CallOutcome`` object representing
-    the exception or result outcome of the inner calls (including other hookwrapper
-    calls).
-    """
-    def setattr_hookimpl_opts(func):
-        if hookwrapper:
-            func.hookwrapper = True
-        if optionalhook:
-            func.optionalhook = True
-        if tryfirst:
-            func.tryfirst = True
-        if trylast:
-            func.trylast = True
-        return func
-    return setattr_hookimpl_opts
-
-
-class TagTracer:
-    def __init__(self):
-        self._tag2proc = {}
-        self.writer = None
-        self.indent = 0
-
-    def get(self, name):
-        return TagTracerSub(self, (name,))
-
-    def format_message(self, tags, args):
-        if isinstance(args[-1], dict):
-            extra = args[-1]
-            args = args[:-1]
-        else:
-            extra = {}
-
-        content = " ".join(map(str, args))
-        indent = "  " * self.indent
-
-        lines = [
-            "%s%s [%s]\n" %(indent, content, ":".join(tags))
-        ]
-
-        for name, value in extra.items():
-            lines.append("%s    %s: %s\n" % (indent, name, value))
-        return lines
-
-    def processmessage(self, tags, args):
-        if self.writer is not None and args:
-            lines = self.format_message(tags, args)
-            self.writer(''.join(lines))
-        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,))
-
-
-def raise_wrapfail(wrap_controller, msg):
-    co = wrap_controller.gi_code
-    raise RuntimeError("wrap_controller at %r %s:%d %s" %
-                   (co.co_name, co.co_filename, co.co_firstlineno, msg))
-
-
-def wrapped_call(wrap_controller, func):
-    """ Wrap calling to a function with a generator which needs to yield
-    exactly once.  The yield point will trigger calling the wrapped function
-    and return its CallOutcome to the yield point.  The generator then needs
-    to finish (raise StopIteration) in order for the wrapped call to complete.
-    """
-    try:
-        next(wrap_controller)   # first yield
-    except StopIteration:
-        raise_wrapfail(wrap_controller, "did not yield")
-    call_outcome = CallOutcome(func)
-    try:
-        wrap_controller.send(call_outcome)
-        raise_wrapfail(wrap_controller, "has second yield")
-    except StopIteration:
-        pass
-    return call_outcome.get_result()
-
-
-class CallOutcome:
-    """ Outcome of a function call, either an exception or a proper result.
-    Calling the ``get_result`` method will return the result or reraise
-    the exception raised when the function was called. """
-    excinfo = None
-    def __init__(self, func):
-        try:
-            self.result = func()
-        except BaseException:
-            self.excinfo = sys.exc_info()
-
-    def force_result(self, result):
-        self.result = result
-        self.excinfo = None
-
-    def get_result(self):
-        if self.excinfo is None:
-            return self.result
-        else:
-            ex = self.excinfo
-            if py3:
-                raise ex[1].with_traceback(ex[2])
-            py.builtin._reraise(*ex)
-
-
-class TracedHookExecution:
-    def __init__(self, pluginmanager, before, after):
-        self.pluginmanager = pluginmanager
-        self.before = before
-        self.after = after
-        self.oldcall = pluginmanager._inner_hookexec
-        assert not isinstance(self.oldcall, TracedHookExecution)
-        self.pluginmanager._inner_hookexec = self
-
-    def __call__(self, hook, methods, kwargs):
-        self.before(hook, methods, kwargs)
-        outcome = CallOutcome(lambda: self.oldcall(hook, methods, kwargs))
-        self.after(outcome, hook, methods, kwargs)
-        return outcome.get_result()
-
-    def undo(self):
-        self.pluginmanager._inner_hookexec = self.oldcall
-
-
-class PluginManager(object):
-    """ Core Pluginmanager class which manages registration
-    of plugin objects and 1:N hook calling.
-
-    You can register new hooks by calling ``addhooks(module_or_class)``.
-    You can register plugin objects (which contain hooks) by calling
-    ``register(plugin)``.  The Pluginmanager is initialized with a
-    prefix that is searched for in the names of the dict of registered
-    plugin objects.  An optional excludefunc allows to blacklist names which
-    are not considered as hooks despite a matching prefix.
-
-    For debugging purposes you can call ``enable_tracing()``
-    which will subsequently send debug information to the trace helper.
-    """
-
-    def __init__(self, prefix, excludefunc=None):
-        self._prefix = prefix
-        self._excludefunc = excludefunc
-        self._name2plugin = {}
-        self._plugin2hookcallers = {}
-        self._plugin_distinfo = []
-        self.trace = TagTracer().get("pluginmanage")
-        self.hook = HookRelay(self.trace.root.get("hook"))
-        self._inner_hookexec = lambda hook, methods, kwargs: \
-                               MultiCall(methods, kwargs, hook.firstresult).execute()
-
-    def _hookexec(self, hook, methods, kwargs):
-        # called from all hookcaller instances.
-        # enable_tracing will set its own wrapping function at self._inner_hookexec
-        return self._inner_hookexec(hook, methods, kwargs)
-
-    def enable_tracing(self):
-        """ enable tracing of hook calls and return an undo function. """
-        hooktrace = self.hook._trace
-
-        def before(hook, methods, kwargs):
-            hooktrace.root.indent += 1
-            hooktrace(hook.name, kwargs)
-
-        def after(outcome, hook, methods, kwargs):
-            if outcome.excinfo is None:
-                hooktrace("finish", hook.name, "-->", outcome.result)
-            hooktrace.root.indent -= 1
-
-        return TracedHookExecution(self, before, after).undo
-
-    def subset_hook_caller(self, name, remove_plugins):
-        """ Return a new HookCaller instance for the named method
-        which manages calls to all registered plugins except the
-        ones from remove_plugins. """
-        orig = getattr(self.hook, name)
-        plugins_to_remove = [plugin for plugin in remove_plugins
-                                    if hasattr(plugin, name)]
-        if plugins_to_remove:
-            hc = HookCaller(orig.name, orig._hookexec, orig._specmodule_or_class)
-            for plugin in orig._plugins:
-                if plugin not in plugins_to_remove:
-                    hc._add_plugin(plugin)
-                    # we also keep track of this hook caller so it
-                    # gets properly removed on plugin unregistration
-                    self._plugin2hookcallers.setdefault(plugin, []).append(hc)
-            return hc
-        return orig
-
-    def register(self, plugin, name=None):
-        """ Register a plugin and return its canonical name or None if the name
-        is blocked from registering.  Raise a ValueError if the plugin is already
-        registered. """
-        plugin_name = name or self.get_canonical_name(plugin)
-
-        if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
-            if self._name2plugin.get(plugin_name, -1) is None:
-                return  # blocked plugin, return None to indicate no registration
-            raise ValueError("Plugin already registered: %s=%s\n%s" %(
-                              plugin_name, plugin, self._name2plugin))
-
-        self._name2plugin[plugin_name] = plugin
-
-        # register prefix-matching hook specs of the plugin
-        self._plugin2hookcallers[plugin] = hookcallers = []
-        for name in dir(plugin):
-            if name.startswith(self._prefix):
-                hook = getattr(self.hook, name, None)
-                if hook is None:
-                    if self._excludefunc is not None and self._excludefunc(name):
-                        continue
-                    hook = HookCaller(name, self._hookexec)
-                    setattr(self.hook, name, hook)
-                elif hook.has_spec():
-                    self._verify_hook(hook, plugin)
-                    hook._maybe_apply_history(getattr(plugin, name))
-                hookcallers.append(hook)
-                hook._add_plugin(plugin)
-        return plugin_name
-
-    def unregister(self, plugin=None, name=None):
-        """ unregister a plugin object and all its contained hook implementations
-        from internal data structures. """
-        if name is None:
-            assert plugin is not None, "one of name or plugin needs to be specified"
-            name = self.get_name(plugin)
-
-        if plugin is None:
-            plugin = self.get_plugin(name)
-
-        # if self._name2plugin[name] == None registration was blocked: ignore
-        if self._name2plugin.get(name):
-            del self._name2plugin[name]
-
-        for hookcaller in self._plugin2hookcallers.pop(plugin, []):
-            hookcaller._remove_plugin(plugin)
-
-        return plugin
-
-    def set_blocked(self, name):
-        """ block registrations of the given name, unregister if already registered. """
-        self.unregister(name=name)
-        self._name2plugin[name] = None
-
-    def addhooks(self, module_or_class):
-        """ add new hook definitions from the given module_or_class using
-        the prefix/excludefunc with which the PluginManager was initialized. """
-        names = []
-        for name in dir(module_or_class):
-            if name.startswith(self._prefix):
-                hc = getattr(self.hook, name, None)
-                if hc is None:
-                    hc = HookCaller(name, self._hookexec, module_or_class)
-                    setattr(self.hook, name, hc)
-                else:
-                    # plugins registered this hook without knowing the spec
-                    hc.set_specification(module_or_class)
-                    for plugin in hc._plugins:
-                        self._verify_hook(hc, plugin)
-                names.append(name)
-
-        if not names:
-            raise ValueError("did not find new %r hooks in %r"
-                             %(self._prefix, module_or_class))
-
-    def get_plugins(self):
-        """ return the set of registered plugins. """
-        return set(self._plugin2hookcallers)
-
-    def is_registered(self, plugin):
-        """ Return True if the plugin is already registered. """
-        return plugin in self._plugin2hookcallers
-
-    def get_canonical_name(self, plugin):
-        """ Return canonical name for a plugin object. Note that a plugin
-        may be registered under a different name which was specified
-        by the caller of register(plugin, name). To obtain the name
-        of an registered plugin use ``get_name(plugin)`` instead."""
-        return getattr(plugin, "__name__", None) or str(id(plugin))
-
-    def get_plugin(self, name):
-        """ Return a plugin or None for the given name. """
-        return self._name2plugin.get(name)
-
-    def get_name(self, plugin):
-        """ Return name for registered plugin or None if not registered. """
-        for name, val in self._name2plugin.items():
-            if plugin == val:
-                return name
-
-    def _verify_hook(self, hook, plugin):
-        method = getattr(plugin, hook.name)
-        pluginname = self.get_name(plugin)
-
-        if hook.is_historic() and hasattr(method, "hookwrapper"):
-            raise PluginValidationError(
-                "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper" %(
-                 pluginname, hook.name))
-
-        for arg in varnames(method):
-            if arg not in hook.argnames:
-                raise PluginValidationError(
-                    "Plugin %r\nhook %r\nargument %r not available\n"
-                     "plugin definition: %s\n"
-                     "available hookargs: %s" %(
-                     pluginname, hook.name, arg, formatdef(method),
-                       ", ".join(hook.argnames)))
-
-    def check_pending(self):
-        """ Verify that all hooks which have not been verified against
-        a hook specification are optional, otherwise raise PluginValidationError"""
-        for name in self.hook.__dict__:
-            if name.startswith(self._prefix):
-                hook = getattr(self.hook, name)
-                if not hook.has_spec():
-                    for plugin in hook._plugins:
-                        method = getattr(plugin, hook.name)
-                        if not getattr(method, "optionalhook", False):
-                            raise PluginValidationError(
-                                "unknown hook %r in plugin %r" %(name, plugin))
-
-    def load_setuptools_entrypoints(self, entrypoint_name):
-        """ Load modules from querying the specified setuptools entrypoint name.
-        Return the number of loaded plugins. """
-        from pkg_resources import iter_entry_points, DistributionNotFound
-        for ep in iter_entry_points(entrypoint_name):
-            # is the plugin registered or blocked?
-            if self.get_plugin(ep.name) or ep.name in self._name2plugin:
-                continue
-            try:
-                plugin = ep.load()
-            except DistributionNotFound:
-                continue
-            self.register(plugin, name=ep.name)
-            self._plugin_distinfo.append((ep.dist, plugin))
-        return len(self._plugin_distinfo)
-
-
-class MultiCall:
-    """ execute a call into multiple python functions/methods. """
-
-    # XXX note that the __multicall__ argument is supported only
-    # for pytest compatibility reasons.  It was never officially
-    # supported there and is explicitely deprecated since 2.8
-    # so we can remove it soon, allowing to avoid the below recursion
-    # in execute() and simplify/speed up the execute loop.
-
-    def __init__(self, methods, kwargs, firstresult=False):
-        self.methods = methods
-        self.kwargs = kwargs
-        self.kwargs["__multicall__"] = self
-        self.firstresult = firstresult
-
-    def execute(self):
-        all_kwargs = self.kwargs
-        self.results = results = []
-        firstresult = self.firstresult
-
-        while self.methods:
-            method = self.methods.pop()
-            args = [all_kwargs[argname] for argname in varnames(method)]
-            if hasattr(method, "hookwrapper"):
-                return wrapped_call(method(*args), self.execute)
-            res = method(*args)
-            if res is not None:
-                if firstresult:
-                    return res
-                results.append(res)
-
-        if not firstresult:
-            return results
-
-    def __repr__(self):
-        status = "%d meths" % (len(self.methods),)
-        if hasattr(self, "results"):
-            status = ("%d results, " % len(self.results)) + status
-        return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
-
-
-
-def varnames(func, startindex=None):
-    """ return argument name tuple for a function, method, class or callable.
-
-    In case of a class, its "__init__" method is considered.
-    For methods the "self" parameter is not included unless you are passing
-    an unbound method with Python3 (which has no supports for unbound methods)
-    """
-    cache = getattr(func, "__dict__", {})
-    try:
-        return cache["_varnames"]
-    except KeyError:
-        pass
-    if isclass(func):
-        try:
-            func = func.__init__
-        except AttributeError:
-            return ()
-        startindex = 1
-    else:
-        if not isfunction(func) and not ismethod(func):
-            func = getattr(func, '__call__', func)
-        if startindex is None:
-            startindex = int(ismethod(func))
-
-    rawcode = py.code.getrawcode(func)
-    try:
-        x = rawcode.co_varnames[startindex:rawcode.co_argcount]
-    except AttributeError:
-        x = ()
-    else:
-        defaults = func.__defaults__
-        if defaults:
-            x = x[:-len(defaults)]
-    try:
-        cache["_varnames"] = x
-    except TypeError:
-        pass
-    return x
-
-
-class HookRelay:
-    def __init__(self, trace):
-        self._trace = trace
-
-
-class HookCaller(object):
-    def __init__(self, name, hook_execute, specmodule_or_class=None):
-        self.name = name
-        self._plugins = []
-        self._wrappers = []
-        self._nonwrappers = []
-        self._hookexec = hook_execute
-        if specmodule_or_class is not None:
-            self.set_specification(specmodule_or_class)
-
-    def has_spec(self):
-        return hasattr(self, "_specmodule_or_class")
-
-    def set_specification(self, specmodule_or_class):
-        assert not self.has_spec()
-        self._specmodule_or_class = specmodule_or_class
-        specfunc = getattr(specmodule_or_class, self.name)
-        argnames = varnames(specfunc, startindex=isclass(specmodule_or_class))
-        assert "self" not in argnames  # sanity check
-        self.argnames = ["__multicall__"] + list(argnames)
-        self.firstresult = getattr(specfunc, 'firstresult', False)
-        if hasattr(specfunc, "historic"):
-            self._call_history = []
-
-    def is_historic(self):
-        return hasattr(self, "_call_history")
-
-    def _remove_plugin(self, plugin):
-        self._plugins.remove(plugin)
-        meth = getattr(plugin, self.name)
-        try:
-            self._nonwrappers.remove(meth)
-        except ValueError:
-            self._wrappers.remove(meth)
-
-    def _add_plugin(self, plugin):
-        self._plugins.append(plugin)
-        self._add_method(getattr(plugin, self.name))
-
-    def _add_method(self, meth):
-        if hasattr(meth, 'hookwrapper'):
-            methods = self._wrappers
-        else:
-            methods = self._nonwrappers
-
-        if hasattr(meth, 'trylast'):
-            methods.insert(0, meth)
-        elif hasattr(meth, 'tryfirst'):
-            methods.append(meth)
-        else:
-            # find last non-tryfirst method
-            i = len(methods) - 1
-            while i >= 0 and hasattr(methods[i], "tryfirst"):
-                i -= 1
-            methods.insert(i + 1, meth)
-
-    def __repr__(self):
-        return "<HookCaller %r>" %(self.name,)
-
-    def __call__(self, **kwargs):
-        assert not self.is_historic()
-        return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
-    def call_historic(self, proc=None, kwargs=None):
-        self._call_history.append((kwargs or {}, proc))
-        # historizing hooks don't return results
-        self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
-    def call_extra(self, methods, kwargs):
-        """ Call the hook with some additional temporarily participating
-        methods using the specified kwargs as call parameters. """
-        old = list(self._nonwrappers), list(self._wrappers)
-        for method in methods:
-            self._add_method(method)
-        try:
-            return self(**kwargs)
-        finally:
-            self._nonwrappers, self._wrappers = old
-
-    def _maybe_apply_history(self, method):
-        if self.is_historic():
-            for kwargs, proc in self._call_history:
-                res = self._hookexec(self, [method], kwargs)
-                if res and proc is not None:
-                    proc(res[0])
-
-
-class PluginValidationError(Exception):
-    """ plugin failed validation. """
-
-
-def formatdef(func):
-    return "%s%s" % (
-        func.__name__,
-        formatargspec(*getargspec(func))
-    )

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -4,6 +4,7 @@
 import pkgutil
 
 import py
+import pluggy
 
 import _pytest
 
@@ -69,7 +70,7 @@
     genscript = config.getvalue("genscript")
     if genscript:
         tw = py.io.TerminalWriter()
-        deps =  ['py', '_pytest', 'pytest']
+        deps =  ['py', '_pytest', 'pytest', 'pluggy']
         if sys.version_info < (2,7):
             deps.append("argparse")
             tw.line("generated script will run on python2.6-python3.3++")

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -1,6 +1,6 @@
 """ hook specifications for pytest plugins, invoked from main.py and builtin plugins.  """
 
-from _pytest.core import hookspec_opts
+from pluggy import hookspec_opts
 
 # -------------------------------------------------------------------------
 # Initialization hooks called for every plugin

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/pytester.py
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -13,7 +13,7 @@
 import py
 import pytest
 from py.builtin import print_
-from _pytest.core import TracedHookExecution
+from pluggy import _TracedHookExecution
 
 from _pytest.main import Session, EXIT_OK
 
@@ -198,7 +198,7 @@
             self.calls.append(ParsedCall(hook.name, kwargs))
         def after(outcome, hook, method, kwargs):
             pass
-        executor = TracedHookExecution(pluginmanager, before, after)
+        executor = _TracedHookExecution(pluginmanager, before, after)
         self._undo_wrapping = executor.undo
 
     def finish_recording(self):

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -8,7 +8,11 @@
 from py._code.code import TerminalRepr
 
 import _pytest
-cutdir = py.path.local(_pytest.__file__).dirpath()
+import pluggy
+
+cutdir2 = py.path.local(_pytest.__file__).dirpath()
+cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
+
 
 NoneType = type(None)
 NOTSET = object()
@@ -18,6 +22,11 @@
 # used to work around a python2 exception info leak
 exc_clear = getattr(sys, 'exc_clear', lambda: None)
 
+
+def filter_traceback(entry):
+    return entry.path != cutdir1 and not entry.path.relto(cutdir2)
+
+
 def getfslineno(obj):
     # xxx let decorators etc specify a sane ordering
     while hasattr(obj, "__wrapped__"):
@@ -604,7 +613,11 @@
             if ntraceback == traceback:
                 ntraceback = ntraceback.cut(path=path)
                 if ntraceback == traceback:
-                    ntraceback = ntraceback.cut(excludepath=cutdir)
+                    #ntraceback = ntraceback.cut(excludepath=cutdir2)
+                    ntraceback = ntraceback.filter(filter_traceback)
+                    if not ntraceback:
+                        ntraceback = traceback
+
             excinfo.traceback = ntraceback.filter()
             # issue364: mark all but first and last frames to
             # only show a single-line message for each frame

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -12,7 +12,7 @@
 # else we are imported
 
 from _pytest.config import main, UsageError, _preloadplugins, cmdline
-from _pytest.core import hookspec_opts, hookimpl_opts
+from pluggy import hookspec_opts, hookimpl_opts
 from _pytest import __version__
 
 _preloadplugins() # to populate pytest.* namespace so help(pytest) works

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf setup.py
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@
 
 
 def main():
-    install_requires = ['py>=1.4.27.dev2']
+    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.1.0,<0.2.0']
     extras_require = {}
     if has_environment_marker_support():
         extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']

diff -r bcbb77bff338c564d2b28500d0444932a04ad35a -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf testing/test_core.py
--- a/testing/test_core.py
+++ /dev/null
@@ -1,1050 +0,0 @@
-import pytest, py, os
-from _pytest.core import * # noqa
-from _pytest.config import get_config
-
-
- at pytest.fixture
-def pm():
-    return PluginManager("he")
-
- at pytest.fixture
-def pytestpm():
-    return PytestPluginManager()
-
-
-class TestPluginManager:
-    def test_plugin_double_register(self, pm):
-        pm.register(42, name="abc")
-        with pytest.raises(ValueError):
-            pm.register(42, name="abc")
-        with pytest.raises(ValueError):
-            pm.register(42, name="def")
-
-    def test_pm(self, pm):
-        class A: pass
-        a1, a2 = A(), A()
-        pm.register(a1)
-        assert pm.is_registered(a1)
-        pm.register(a2, "hello")
-        assert pm.is_registered(a2)
-        l = pm.get_plugins()
-        assert a1 in l
-        assert a2 in l
-        assert pm.get_plugin('hello') == a2
-        assert pm.unregister(a1) == a1
-        assert not pm.is_registered(a1)
-
-    def test_pm_name(self, pm):
-        class A: pass
-        a1 = A()
-        name = pm.register(a1, name="hello")
-        assert name == "hello"
-        pm.unregister(a1)
-        assert pm.get_plugin(a1) is None
-        assert not pm.is_registered(a1)
-        assert not pm.get_plugins()
-        name2 = pm.register(a1, name="hello")
-        assert name2 == name
-        pm.unregister(name="hello")
-        assert pm.get_plugin(a1) is None
-        assert not pm.is_registered(a1)
-        assert not pm.get_plugins()
-
-    def test_set_blocked(self, pm):
-        class A: pass
-        a1 = A()
-        name = pm.register(a1)
-        assert pm.is_registered(a1)
-        pm.set_blocked(name)
-        assert not pm.is_registered(a1)
-
-        pm.set_blocked("somename")
-        assert not pm.register(A(), "somename")
-        pm.unregister(name="somename")
-
-    def test_register_mismatch_method(self, pytestpm):
-        class hello:
-            def pytest_gurgel(self):
-                pass
-        pytestpm.register(hello())
-        with pytest.raises(PluginValidationError):
-            pytestpm.check_pending()
-
-    def test_register_mismatch_arg(self):
-        pm = get_config().pluginmanager
-        class hello:
-            def pytest_configure(self, asd):
-                pass
-        pytest.raises(Exception, lambda: pm.register(hello()))
-
-    def test_register(self):
-        pm = get_config().pluginmanager
-        class MyPlugin:
-            pass
-        my = MyPlugin()
-        pm.register(my)
-        assert pm.get_plugins()
-        my2 = MyPlugin()
-        pm.register(my2)
-        assert set([my,my2]).issubset(pm.get_plugins())
-
-        assert pm.is_registered(my)
-        assert pm.is_registered(my2)
-        pm.unregister(my)
-        assert not pm.is_registered(my)
-        assert my not in pm.get_plugins()
-
-    def test_register_unknown_hooks(self, pm):
-        class Plugin1:
-            def he_method1(self, arg):
-                return arg + 1
-
-        pm.register(Plugin1())
-        class Hooks:
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-        #assert not pm._unverified_hooks
-        assert pm.hook.he_method1(arg=1) == [2]
-
-    def test_register_historic(self, pm):
-        class Hooks:
-            @hookspec_opts(historic=True)
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-
-        pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
-        l = []
-        class Plugin:
-            def he_method1(self, arg):
-                l.append(arg)
-
-        pm.register(Plugin())
-        assert l == [1]
-
-        class Plugin2:
-            def he_method1(self, arg):
-                l.append(arg*10)
-        pm.register(Plugin2())
-        assert l == [1, 10]
-        pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
-        assert l == [1, 10, 120, 12]
-
-    def test_with_result_memorized(self, pm):
-        class Hooks:
-            @hookspec_opts(historic=True)
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-
-        he_method1 = pm.hook.he_method1
-        he_method1.call_historic(lambda res: l.append(res), dict(arg=1))
-        l = []
-        class Plugin:
-            def he_method1(self, arg):
-                return arg * 10
-
-        pm.register(Plugin())
-        assert l == [10]
-
-    def test_register_historic_incompat_hookwrapper(self, pm):
-        class Hooks:
-            @hookspec_opts(historic=True)
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-
-        l = []
-        class Plugin:
-            @hookimpl_opts(hookwrapper=True)
-            def he_method1(self, arg):
-                l.append(arg)
-
-        with pytest.raises(PluginValidationError):
-            pm.register(Plugin())
-
-    def test_call_extra(self, pm):
-        class Hooks:
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-
-        def he_method1(arg):
-            return arg * 10
-
-        l = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
-        assert l == [10]
-
-    def test_subset_hook_caller(self, pm):
-        class Hooks:
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-
-        l = []
-        class Plugin1:
-            def he_method1(self, arg):
-                l.append(arg)
-        class Plugin2:
-            def he_method1(self, arg):
-                l.append(arg*10)
-        class PluginNo:
-            pass
-
-        plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
-        pm.register(plugin1)
-        pm.register(plugin2)
-        pm.register(plugin3)
-        pm.hook.he_method1(arg=1)
-        assert l == [10, 1]
-        l[:] = []
-
-        hc = pm.subset_hook_caller("he_method1", [plugin1])
-        hc(arg=2)
-        assert l == [20]
-        l[:] = []
-
-        hc = pm.subset_hook_caller("he_method1", [plugin2])
-        hc(arg=2)
-        assert l == [2]
-        l[:] = []
-
-        pm.unregister(plugin1)
-        hc(arg=2)
-        assert l == []
-        l[:] = []
-
-        pm.hook.he_method1(arg=1)
-        assert l == [10]
-
-
-
-class TestAddMethodOrdering:
-    @pytest.fixture
-    def hc(self, pm):
-        class Hooks:
-            def he_method1(self, arg):
-                pass
-        pm.addhooks(Hooks)
-        return pm.hook.he_method1
-
-    @pytest.fixture
-    def addmeth(self, hc):
-        def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
-            def wrap(func):
-                if tryfirst:
-                    func.tryfirst = True
-                if trylast:
-                    func.trylast = True
-                if hookwrapper:
-                    func.hookwrapper = True
-                hc._add_method(func)
-                return func
-            return wrap
-        return addmeth
-
-    def test_adding_nonwrappers(self, hc, addmeth):
-        @addmeth()
-        def he_method1():
-            pass
-
-        @addmeth()
-        def he_method2():
-            pass
-
-        @addmeth()
-        def he_method3():
-            pass
-        assert hc._nonwrappers == [he_method1, he_method2, he_method3]
-
-    def test_adding_nonwrappers_trylast(self, hc, addmeth):
-        @addmeth()
-        def he_method1_middle():
-            pass
-
-        @addmeth(trylast=True)
-        def he_method1():
-            pass
-
-        @addmeth()
-        def he_method1_b():
-            pass
-        assert hc._nonwrappers == [he_method1, he_method1_middle, he_method1_b]
-
-    def test_adding_nonwrappers_trylast3(self, hc, addmeth):
-        @addmeth()
-        def he_method1_a():
-            pass
-
-        @addmeth(trylast=True)
-        def he_method1_b():
-            pass
-
-        @addmeth()
-        def he_method1_c():
-            pass
-
-        @addmeth(trylast=True)
-        def he_method1_d():
-            pass
-        assert hc._nonwrappers == [he_method1_d, he_method1_b,
-                                   he_method1_a, he_method1_c]
-
-
-    def test_adding_nonwrappers_trylast2(self, hc, addmeth):
-        @addmeth()
-        def he_method1_middle():
-            pass
-
-        @addmeth()
-        def he_method1_b():
-            pass
-
-        @addmeth(trylast=True)
-        def he_method1():
-            pass
-        assert hc._nonwrappers == [he_method1, he_method1_middle, he_method1_b]
-
-    def test_adding_nonwrappers_tryfirst(self, hc, addmeth):
-        @addmeth(tryfirst=True)
-        def he_method1():
-            pass
-
-        @addmeth()
-        def he_method1_middle():
-            pass
-
-        @addmeth()
-        def he_method1_b():
-            pass
-        assert hc._nonwrappers == [he_method1_middle, he_method1_b, he_method1]
-
-    def test_adding_wrappers_ordering(self, hc, addmeth):
-        @addmeth(hookwrapper=True)
-        def he_method1():
-            pass
-
-        @addmeth()
-        def he_method1_middle():
-            pass
-
-        @addmeth(hookwrapper=True)
-        def he_method3():
-            pass
-
-        assert hc._nonwrappers == [he_method1_middle]
-        assert hc._wrappers == [he_method1, he_method3]
-
-    def test_adding_wrappers_ordering_tryfirst(self, hc, addmeth):
-        @addmeth(hookwrapper=True, tryfirst=True)
-        def he_method1():
-            pass
-
-        @addmeth(hookwrapper=True)
-        def he_method2():
-            pass
-
-        assert hc._nonwrappers == []
-        assert hc._wrappers == [he_method2, he_method1]
-
-
-    def test_hookspec_opts(self, pm):
-        class HookSpec:
-            @hookspec_opts()
-            def he_myhook1(self, arg1):
-                pass
-
-            @hookspec_opts(firstresult=True)
-            def he_myhook2(self, arg1):
-                pass
-
-            @hookspec_opts(firstresult=False)
-            def he_myhook3(self, arg1):
-                pass
-
-        pm.addhooks(HookSpec)
-        assert not pm.hook.he_myhook1.firstresult
-        assert pm.hook.he_myhook2.firstresult
-        assert not pm.hook.he_myhook3.firstresult
-
-
-    def test_hookimpl_opts(self):
-        for name in ["hookwrapper", "optionalhook", "tryfirst", "trylast"]:
-            for val in [True, False]:
-                @hookimpl_opts(**{name: val})
-                def he_myhook1(self, arg1):
-                    pass
-                if val:
-                    assert getattr(he_myhook1, name)
-                else:
-                    assert not hasattr(he_myhook1, name)
-
-    def test_decorator_functional(self, pm):
-        class HookSpec:
-            @hookspec_opts(firstresult=True)
-            def he_myhook(self, arg1):
-                """ add to arg1 """
-        pm.addhooks(HookSpec)
-
-        class Plugin:
-            @hookimpl_opts()
-            def he_myhook(self, arg1):
-                return arg1 + 1
-
-        pm.register(Plugin())
-        results = pm.hook.he_myhook(arg1=17)
-        assert results == 18
-
-    def test_load_setuptools_instantiation(self, monkeypatch, pm):
-        pkg_resources = pytest.importorskip("pkg_resources")
-        def my_iter(name):
-            assert name == "hello"
-            class EntryPoint:
-                name = "myname"
-                dist = None
-                def load(self):
-                    class PseudoPlugin:
-                        x = 42
-                    return PseudoPlugin()
-            return iter([EntryPoint()])
-
-        monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
-        num = pm.load_setuptools_entrypoints("hello")
-        assert num == 1
-        plugin = pm.get_plugin("myname")
-        assert plugin.x == 42
-        assert pm._plugin_distinfo == [(None, plugin)]
-
-    def test_load_setuptools_not_installed(self, monkeypatch, pm):
-        monkeypatch.setitem(py.std.sys.modules, 'pkg_resources',
-            py.std.types.ModuleType("pkg_resources"))
-        with pytest.raises(ImportError):
-            pm.load_setuptools_entrypoints("qwe")
-
-
-class TestPytestPluginInteractions:
-
-    def test_addhooks_conftestplugin(self, testdir):
-        testdir.makepyfile(newhooks="""
-            def pytest_myhook(xyz):
-                "new hook"
-        """)
-        conf = testdir.makeconftest("""
-            import sys ; sys.path.insert(0, '.')
-            import newhooks
-            def pytest_addhooks(pluginmanager):
-                pluginmanager.addhooks(newhooks)
-            def pytest_myhook(xyz):
-                return xyz + 1
-        """)
-        config = get_config()
-        pm = config.pluginmanager
-        pm.hook.pytest_addhooks.call_historic(
-                                kwargs=dict(pluginmanager=config.pluginmanager))
-        config.pluginmanager._importconftest(conf)
-        #print(config.pluginmanager.get_plugins())
-        res = config.hook.pytest_myhook(xyz=10)
-        assert res == [11]
-
-    def test_addhooks_nohooks(self, testdir):
-        testdir.makeconftest("""
-            import sys
-            def pytest_addhooks(pluginmanager):
-                pluginmanager.addhooks(sys)
-        """)
-        res = testdir.runpytest()
-        assert res.ret != 0
-        res.stderr.fnmatch_lines([
-            "*did not find*sys*"
-        ])
-
-    def test_namespace_early_from_import(self, testdir):
-        p = testdir.makepyfile("""
-            from pytest import Item
-            from pytest import Item as Item2
-            assert Item is Item2
-        """)
-        result = testdir.runpython(p)
-        assert result.ret == 0
-
-    def test_do_ext_namespace(self, testdir):
-        testdir.makeconftest("""
-            def pytest_namespace():
-                return {'hello': 'world'}
-        """)
-        p = testdir.makepyfile("""
-            from pytest import hello
-            import pytest
-            def test_hello():
-                assert hello == "world"
-                assert 'hello' in pytest.__all__
-        """)
-        reprec = testdir.inline_run(p)
-        reprec.assertoutcome(passed=1)
-
-    def test_do_option_postinitialize(self, testdir):
-        config = testdir.parseconfigure()
-        assert not hasattr(config.option, 'test123')
-        p = testdir.makepyfile("""
-            def pytest_addoption(parser):
-                parser.addoption('--test123', action="store_true",
-                    default=True)
-        """)
-        config.pluginmanager._importconftest(p)
-        assert config.option.test123
-
-    def test_configure(self, testdir):
-        config = testdir.parseconfig()
-        l = []
-        class A:
-            def pytest_configure(self, config):
-                l.append(self)
-
-        config.pluginmanager.register(A())
-        assert len(l) == 0
-        config._do_configure()
-        assert len(l) == 1
-        config.pluginmanager.register(A())  # leads to a configured() plugin
-        assert len(l) == 2
-        assert l[0] != l[1]
-
-        config._ensure_unconfigure()
-        config.pluginmanager.register(A())
-        assert len(l) == 2
-
-    def test_hook_tracing(self):
-        pytestpm = get_config().pluginmanager  # fully initialized with plugins
-        saveindent = []
-        class api1:
-            def pytest_plugin_registered(self):
-                saveindent.append(pytestpm.trace.root.indent)
-        class api2:
-            def pytest_plugin_registered(self):
-                saveindent.append(pytestpm.trace.root.indent)
-                raise ValueError()
-        l = []
-        pytestpm.trace.root.setwriter(l.append)
-        undo = pytestpm.enable_tracing()
-        try:
-            indent = pytestpm.trace.root.indent
-            p = api1()
-            pytestpm.register(p)
-            assert pytestpm.trace.root.indent == indent
-            assert len(l) >= 2
-            assert 'pytest_plugin_registered' in l[0]
-            assert 'finish' in l[1]
-
-            l[:] = []
-            with pytest.raises(ValueError):
-                pytestpm.register(api2())
-            assert pytestpm.trace.root.indent == indent
-            assert saveindent[0] > indent
-        finally:
-            undo()
-
-    def test_warn_on_deprecated_multicall(self, pytestpm):
-        class Plugin:
-            def pytest_configure(self, __multicall__):
-                pass
-
-        before = list(pytestpm._warnings)
-        pytestpm.register(Plugin())
-        assert len(pytestpm._warnings) == len(before) + 1
-        assert "deprecated" in pytestpm._warnings[-1]["message"]
-
-
-def test_namespace_has_default_and_env_plugins(testdir):
-    p = testdir.makepyfile("""
-        import pytest
-        pytest.mark
-    """)
-    result = testdir.runpython(p)
-    assert result.ret == 0
-
-def test_varnames():
-    def f(x):
-        i = 3  # noqa
-    class A:
-        def f(self, y):
-            pass
-    class B(object):
-        def __call__(self, z):
-            pass
-    assert varnames(f) == ("x",)
-    assert varnames(A().f) == ('y',)
-    assert varnames(B()) == ('z',)
-
-def test_varnames_default():
-    def f(x, y=3):
-        pass
-    assert varnames(f) == ("x",)
-
-def test_varnames_class():
-    class C:
-        def __init__(self, x):
-            pass
-    class D:
-        pass
-    assert varnames(C) == ("x",)
-    assert varnames(D) == ()
-
-class TestMultiCall:
-    def test_uses_copy_of_methods(self):
-        l = [lambda: 42]
-        mc = MultiCall(l, {})
-        repr(mc)
-        l[:] = []
-        res = mc.execute()
-        return res == 42
-
-    def test_call_passing(self):
-        class P1:
-            def m(self, __multicall__, x):
-                assert len(__multicall__.results) == 1
-                assert not __multicall__.methods
-                return 17
-
-        class P2:
-            def m(self, __multicall__, x):
-                assert __multicall__.results == []
-                assert __multicall__.methods
-                return 23
-
-        p1 = P1()
-        p2 = P2()
-        multicall = MultiCall([p1.m, p2.m], {'x': 23})
-        assert "23" in repr(multicall)
-        reslist = multicall.execute()
-        assert len(reslist) == 2
-        # ensure reversed order
-        assert reslist == [23, 17]
-
-    def test_keyword_args(self):
-        def f(x):
-            return x + 1
-        class A:
-            def f(self, x, y):
-                return x + y
-        multicall = MultiCall([f, A().f], dict(x=23, y=24))
-        assert "'x': 23" in repr(multicall)
-        assert "'y': 24" in repr(multicall)
-        reslist = multicall.execute()
-        assert reslist == [24+23, 24]
-        assert "2 results" in repr(multicall)
-
-    def test_keyword_args_with_defaultargs(self):
-        def f(x, z=1):
-            return x + z
-        reslist = MultiCall([f], dict(x=23, y=24)).execute()
-        assert reslist == [24]
-
-    def test_tags_call_error(self):
-        multicall = MultiCall([lambda x: x], {})
-        pytest.raises(KeyError, multicall.execute)
-
-    def test_call_subexecute(self):
-        def m(__multicall__):
-            subresult = __multicall__.execute()
-            return subresult + 1
-
-        def n():
-            return 1
-
-        call = MultiCall([n, m], {}, firstresult=True)
-        res = call.execute()
-        assert res == 2
-
-    def test_call_none_is_no_result(self):
-        def m1():
-            return 1
-        def m2():
-            return None
-        res = MultiCall([m1, m2], {}, firstresult=True).execute()
-        assert res == 1
-        res = MultiCall([m1, m2], {}).execute()
-        assert res == [1]
-
-    def test_hookwrapper(self):
-        l = []
-        def m1():
-            l.append("m1 init")
-            yield None
-            l.append("m1 finish")
-        m1.hookwrapper = True
-
-        def m2():
-            l.append("m2")
-            return 2
-        res = MultiCall([m2, m1], {}).execute()
-        assert res == [2]
-        assert l == ["m1 init", "m2", "m1 finish"]
-        l[:] = []
-        res = MultiCall([m2, m1], {}, firstresult=True).execute()
-        assert res == 2
-        assert l == ["m1 init", "m2", "m1 finish"]
-
-    def test_hookwrapper_order(self):
-        l = []
-        def m1():
-            l.append("m1 init")
-            yield 1
-            l.append("m1 finish")
-        m1.hookwrapper = True
-
-        def m2():
-            l.append("m2 init")
-            yield 2
-            l.append("m2 finish")
-        m2.hookwrapper = True
-        res = MultiCall([m2, m1], {}).execute()
-        assert res == []
-        assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
-
-    def test_hookwrapper_not_yield(self):
-        def m1():
-            pass
-        m1.hookwrapper = True
-
-        mc = MultiCall([m1], {})
-        with pytest.raises(TypeError):
-            mc.execute()
-
-    def test_hookwrapper_too_many_yield(self):
-        def m1():
-            yield 1
-            yield 2
-        m1.hookwrapper = True
-
-        mc = MultiCall([m1], {})
-        with pytest.raises(RuntimeError) as ex:
-            mc.execute()
-        assert "m1" in str(ex.value)
-        assert "test_core.py:" in str(ex.value)
-
-    @pytest.mark.parametrize("exc", [ValueError, SystemExit])
-    def test_hookwrapper_exception(self, exc):
-        l = []
-        def m1():
-            l.append("m1 init")
-            yield None
-            l.append("m1 finish")
-        m1.hookwrapper = True
-
-        def m2():
-            raise exc
-        with pytest.raises(exc):
-            MultiCall([m2, m1], {}).execute()
-        assert l == ["m1 init", "m1 finish"]
-
-
-class TestHookRelay:
-    def test_hapmypath(self):
-        class Api:
-            def hello(self, arg):
-                "api hook 1"
-        pm = PluginManager("he")
-        pm.addhooks(Api)
-        hook = pm.hook
-        assert hasattr(hook, 'hello')
-        assert repr(hook.hello).find("hello") != -1
-        class Plugin:
-            def hello(self, arg):
-                return arg + 1
-        plugin = Plugin()
-        pm.register(plugin)
-        l = hook.hello(arg=3)
-        assert l == [4]
-        assert not hasattr(hook, 'world')
-        pm.unregister(plugin)
-        assert hook.hello(arg=3) == []
-
-    def test_argmismatch(self):
-        class Api:
-            def hello(self, arg):
-                "api hook 1"
-        pm = PluginManager("he")
-        pm.addhooks(Api)
-        class Plugin:
-            def hello(self, argwrong):
-                return arg + 1
-        with pytest.raises(PluginValidationError) as exc:
-            pm.register(Plugin())
-        assert "argwrong" in str(exc.value)
-
-    def test_only_kwargs(self):
-        pm = PluginManager("he")
-        class Api:
-            def hello(self, arg):
-                "api hook 1"
-        pm.addhooks(Api)
-        pytest.raises(TypeError, lambda: pm.hook.hello(3))
-
-    def test_firstresult_definition(self):
-        class Api:
-            def hello(self, arg):
-                "api hook 1"
-            hello.firstresult = True
-        pm = PluginManager("he")
-        pm.addhooks(Api)
-        class Plugin:
-            def hello(self, arg):
-                return arg + 1
-        pm.register(Plugin())
-        res = pm.hook.hello(arg=3)
-        assert res == 4
-
-class TestTracer:
-    def test_simple(self):
-        from _pytest.core import TagTracer
-        rootlogger = TagTracer()
-        log = rootlogger.get("pytest")
-        log("hello")
-        l = []
-        rootlogger.setwriter(l.append)
-        log("world")
-        assert len(l) == 1
-        assert l[0] == "world [pytest]\n"
-        sublog = log.get("collection")
-        sublog("hello")
-        assert l[1] == "hello [pytest:collection]\n"
-
-    def test_indent(self):
-        from _pytest.core import TagTracer
-        rootlogger = TagTracer()
-        log = rootlogger.get("1")
-        l = []
-        log.root.setwriter(lambda arg: l.append(arg))
-        log("hello")
-        log.root.indent += 1
-        log("line1")
-        log("line2")
-        log.root.indent += 1
-        log("line3")
-        log("line4")
-        log.root.indent -= 1
-        log("line5")
-        log.root.indent -= 1
-        log("last")
-        assert len(l) == 7
-        names = [x[:x.rfind(' [')] for x in l]
-        assert names == ['hello', '  line1', '  line2',
-                     '    line3', '    line4', '  line5', 'last']
-
-    def test_readable_output_dictargs(self):
-        from _pytest.core import TagTracer
-        rootlogger = TagTracer()
-
-        out = rootlogger.format_message(['test'], [1])
-        assert out == ['1 [test]\n']
-
-        out2= rootlogger.format_message(['test'], ['test', {'a':1}])
-        assert out2 ==[
-            'test [test]\n',
-            '    a: 1\n'
-        ]
-
-    def test_setprocessor(self):
-        from _pytest.core import TagTracer
-        rootlogger = TagTracer()
-        log = rootlogger.get("1")
-        log2 = log.get("2")
-        assert log2.tags  == tuple("12")
-        l = []
-        rootlogger.setprocessor(tuple("12"), lambda *args: l.append(args))
-        log("not seen")
-        log2("seen")
-        assert len(l) == 1
-        tags, args = l[0]
-        assert "1" in tags
-        assert "2" in tags
-        assert args == ("seen",)
-        l2 = []
-        rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
-        log2("seen")
-        tags, args = l2[0]
-        assert args == ("seen",)
-
-
-    def test_setmyprocessor(self):
-        from _pytest.core import TagTracer
-        rootlogger = TagTracer()
-        log = rootlogger.get("1")
-        log2 = log.get("2")
-        l = []
-        log2.setmyprocessor(lambda *args: l.append(args))
-        log("not seen")
-        assert not l
-        log2(42)
-        assert len(l) == 1
-        tags, args = l[0]
-        assert "1" in tags
-        assert "2" in tags
-        assert args == (42,)
-
-def test_default_markers(testdir):
-    result = testdir.runpytest("--markers")
-    result.stdout.fnmatch_lines([
-        "*tryfirst*first*",
-        "*trylast*last*",
-    ])
-
-def test_importplugin_issue375(testdir, pytestpm):
-    testdir.syspathinsert(testdir.tmpdir)
-    testdir.makepyfile(qwe="import aaaa")
-    with pytest.raises(ImportError) as excinfo:
-        pytestpm.import_plugin("qwe")
-    assert "qwe" not in str(excinfo.value)
-    assert "aaaa" in str(excinfo.value)
-
-
-### to be shifted to own test file
-from _pytest.config import PytestPluginManager
-
-class TestPytestPluginManager:
-    def test_register_imported_modules(self):
-        pm = PytestPluginManager()
-        mod = py.std.types.ModuleType("x.y.pytest_hello")
-        pm.register(mod)
-        assert pm.is_registered(mod)
-        l = pm.get_plugins()
-        assert mod in l
-        pytest.raises(ValueError, "pm.register(mod)")
-        pytest.raises(ValueError, lambda: pm.register(mod))
-        #assert not pm.is_registered(mod2)
-        assert pm.get_plugins() == l
-
-    def test_canonical_import(self, monkeypatch):
-        mod = py.std.types.ModuleType("pytest_xyz")
-        monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod)
-        pm = PytestPluginManager()
-        pm.import_plugin('pytest_xyz')
-        assert pm.get_plugin('pytest_xyz') == mod
-        assert pm.is_registered(mod)
-
-    def test_consider_module(self, testdir, pytestpm):
-        testdir.syspathinsert()
-        testdir.makepyfile(pytest_p1="#")
-        testdir.makepyfile(pytest_p2="#")
-        mod = py.std.types.ModuleType("temp")
-        mod.pytest_plugins = ["pytest_p1", "pytest_p2"]
-        pytestpm.consider_module(mod)
-        assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1"
-        assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2"
-
-    def test_consider_module_import_module(self, testdir):
-        pytestpm = get_config().pluginmanager
-        mod = py.std.types.ModuleType("x")
-        mod.pytest_plugins = "pytest_a"
-        aplugin = testdir.makepyfile(pytest_a="#")
-        reprec = testdir.make_hook_recorder(pytestpm)
-        #syspath.prepend(aplugin.dirpath())
-        py.std.sys.path.insert(0, str(aplugin.dirpath()))
-        pytestpm.consider_module(mod)
-        call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
-        assert call.plugin.__name__ == "pytest_a"
-
-        # check that it is not registered twice
-        pytestpm.consider_module(mod)
-        l = reprec.getcalls("pytest_plugin_registered")
-        assert len(l) == 1
-
-    def test_consider_env_fails_to_import(self, monkeypatch, pytestpm):
-        monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
-        with pytest.raises(ImportError):
-            pytestpm.consider_env()
-
-    def test_plugin_skip(self, testdir, monkeypatch):
-        p = testdir.makepyfile(skipping1="""
-            import pytest
-            pytest.skip("hello")
-        """)
-        p.copy(p.dirpath("skipping2.py"))
-        monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
-        result = testdir.runpytest("-rw", "-p", "skipping1", syspathinsert=True)
-        assert result.ret == 0
-        result.stdout.fnmatch_lines([
-            "WI1*skipped plugin*skipping1*hello*",
-            "WI1*skipped plugin*skipping2*hello*",
-        ])
-
-    def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm):
-        testdir.syspathinsert()
-        testdir.makepyfile(xy123="#")
-        monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
-        l1 = len(pytestpm.get_plugins())
-        pytestpm.consider_env()
-        l2 = len(pytestpm.get_plugins())
-        assert l2 == l1 + 1
-        assert pytestpm.get_plugin('xy123')
-        pytestpm.consider_env()
-        l3 = len(pytestpm.get_plugins())
-        assert l2 == l3
-
-    def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
-        testdir.makepyfile(pytest_x500="#")
-        p = testdir.makepyfile("""
-            import pytest
-            def test_hello(pytestconfig):
-                plugin = pytestconfig.pluginmanager.get_plugin('pytest_x500')
-                assert plugin is not None
-        """)
-        monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
-        result = testdir.runpytest(p, syspathinsert=True)
-        assert result.ret == 0
-        result.stdout.fnmatch_lines(["*1 passed*"])
-
-    def test_import_plugin_importname(self, testdir, pytestpm):
-        pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
-        pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwx.y")')
-
-        testdir.syspathinsert()
-        pluginname = "pytest_hello"
-        testdir.makepyfile(**{pluginname: ""})
-        pytestpm.import_plugin("pytest_hello")
-        len1 = len(pytestpm.get_plugins())
-        pytestpm.import_plugin("pytest_hello")
-        len2 = len(pytestpm.get_plugins())
-        assert len1 == len2
-        plugin1 = pytestpm.get_plugin("pytest_hello")
-        assert plugin1.__name__.endswith('pytest_hello')
-        plugin2 = pytestpm.get_plugin("pytest_hello")
-        assert plugin2 is plugin1
-
-    def test_import_plugin_dotted_name(self, testdir, pytestpm):
-        pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
-        pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwex.y")')
-
-        testdir.syspathinsert()
-        testdir.mkpydir("pkg").join("plug.py").write("x=3")
-        pluginname = "pkg.plug"
-        pytestpm.import_plugin(pluginname)
-        mod = pytestpm.get_plugin("pkg.plug")
-        assert mod.x == 3
-
-    def test_consider_conftest_deps(self, testdir, pytestpm):
-        mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
-        with pytest.raises(ImportError):
-            pytestpm.consider_conftest(mod)
-
-
-class TestPytestPluginManagerBootstrapming:
-    def test_preparse_args(self, pytestpm):
-        pytest.raises(ImportError, lambda:
-            pytestpm.consider_preparse(["xyz", "-p", "hello123"]))
-
-    def test_plugin_prevent_register(self, pytestpm):
-        pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
-        l1 = pytestpm.get_plugins()
-        pytestpm.register(42, name="abc")
-        l2 = pytestpm.get_plugins()
-        assert len(l2) == len(l1)
-        assert 42 not in l2
-
-    def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm):
-        pytestpm.register(42, name="abc")
-        l1 = pytestpm.get_plugins()
-        assert 42 in l1
-        pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
-        l2 = pytestpm.get_plugins()
-        assert 42 not in l2

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/pytest-dev/pytest/commits/dd157ad4f3fa/
Changeset:   dd157ad4f3fa
Branch:      pluggy1
User:        hpk42
Date:        2015-04-29 14:40:52+00:00
Summary:     adapt pytest to pluggy's decoratorclass branch
Affected #:  6 files

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -9,7 +9,10 @@
 # DON't import pytest here because it causes import cycle troubles
 import sys, os
 from _pytest import hookspec # the extension point definitions
-from pluggy import PluginManager, hookimpl_opts, varnames
+from pluggy import PluginManager, Hookimpl, Hookspec
+
+hookimpl_opts = Hookimpl("pytest")
+hookspec_opts = Hookspec("pytest")
 
 # pytest startup
 #
@@ -106,10 +109,10 @@
            name.startswith("pytest_funcarg__")
 
 
+
 class PytestPluginManager(PluginManager):
     def __init__(self):
-        super(PytestPluginManager, self).__init__(prefix="pytest_",
-                                                  excludefunc=exclude_pytest_names)
+        super(PytestPluginManager, self).__init__("pytest")
         self._warnings = []
         self._conftest_plugins = set()
 
@@ -130,12 +133,31 @@
             self.trace.root.setwriter(err.write)
             self.enable_tracing()
 
+    def get_hookimpl_opts(self, plugin, name):
+        method = getattr(plugin, name)
+        opts = super(PytestPluginManager, self).get_hookimpl_opts(plugin, name)
+        if opts is None:
+            if name.startswith("pytest_") and not exclude_pytest_names(name):
+                opts = {}
+                opts["tryfirst"] = hasattr(method, "tryfirst")
+                opts["trylast"] = hasattr(method, "trylast")
+                opts["optionalhook"] = hasattr(method, "optionalhook")
+                opts["hookwrapper"] = hasattr(method, "hookwrapper")
+        return opts
 
-    def _verify_hook(self, hook, plugin):
-        super(PytestPluginManager, self)._verify_hook(hook, plugin)
-        method = getattr(plugin, hook.name)
-        if "__multicall__" in varnames(method):
-            fslineno = py.code.getfslineno(method)
+    def get_hookspec_opts(self, module_or_class, name):
+        opts = super(PytestPluginManager, self).get_hookspec_opts(module_or_class, name)
+        if opts is None:
+            if name.startswith("pytest_"):
+                meth = getattr(module_or_class, name)
+                opts = {"firstresult": hasattr(meth, "firstresult"),
+                        "historic": hasattr(meth, "historic")}
+        return opts
+
+    def _verify_hook(self, hook, hookmethod):
+        super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
+        if "__multicall__" in hookmethod.argnames:
+            fslineno = py.code.getfslineno(hookmethod.function)
             warning = dict(code="I1",
                            fslocation=fslineno,
                            message="%r hook uses deprecated __multicall__ "

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -70,7 +70,7 @@
     genscript = config.getvalue("genscript")
     if genscript:
         tw = py.io.TerminalWriter()
-        deps =  ['py', '_pytest', 'pytest', 'pluggy']
+        deps =  ['py', 'pluggy', '_pytest', 'pytest']
         if sys.version_info < (2,7):
             deps.append("argparse")
             tw.line("generated script will run on python2.6-python3.3++")

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -1,6 +1,8 @@
 """ hook specifications for pytest plugins, invoked from main.py and builtin plugins.  """
 
-from pluggy import hookspec_opts
+from pluggy import Hookspec
+
+hookspec_opts = Hookspec("pytest")
 
 # -------------------------------------------------------------------------
 # Initialization hooks called for every plugin

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -501,13 +501,13 @@
     def __init__(self, config):
         FSCollector.__init__(self, config.rootdir, parent=None,
                              config=config, session=self)
-        self.config.pluginmanager.register(self, name="session")
+        self._fs2hookproxy = {}
         self._testsfailed = 0
         self.shouldstop = False
         self.trace = config.trace.root.get("collection")
         self._norecursepatterns = config.getini("norecursedirs")
         self.startdir = py.path.local()
-        self._fs2hookproxy = {}
+        self.config.pluginmanager.register(self, name="session")
 
     def _makeid(self):
         return ""

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -11,8 +11,10 @@
 
 # else we are imported
 
-from _pytest.config import main, UsageError, _preloadplugins, cmdline
-from pluggy import hookspec_opts, hookimpl_opts
+from _pytest.config import (
+    main, UsageError, _preloadplugins, cmdline,
+    hookspec_opts, hookimpl_opts
+)
 from _pytest import __version__
 
 _preloadplugins() # to populate pytest.* namespace so help(pytest) works

diff -r 8d6251cf5ac6dbbf41a3ce8dcf0dba400c769edf -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 testing/test_config.py
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -357,9 +357,9 @@
     pm.register(m)
     hc = pm.hook.pytest_load_initial_conftests
     l = hc._nonwrappers + hc._wrappers
-    assert l[-1].__module__ == "_pytest.capture"
-    assert l[-2] == m.pytest_load_initial_conftests
-    assert l[-3].__module__ == "_pytest.config"
+    assert l[-1].function.__module__ == "_pytest.capture"
+    assert l[-2].function == m.pytest_load_initial_conftests
+    assert l[-3].function.__module__ == "_pytest.config"
 
 class TestWarning:
     def test_warn_config(self, testdir):


https://bitbucket.org/pytest-dev/pytest/commits/339611d37480/
Changeset:   339611d37480
Branch:      pluggy1
User:        hpk42
Date:        2015-05-04 12:42:01+00:00
Summary:     now that we are going to have wheels, py source code might be not be installed
and the resulting genscript is useless
Affected #:  2 files

diff -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 -r 339611d3748054b5a524993370cd78e112f46a58 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -34,6 +34,9 @@
         for pyfile in toplevel.visit('*.py'):
             pkg = pkgname(name, toplevel, pyfile)
             name2src[pkg] = pyfile.read()
+        # with wheels py source code might be not be installed
+        # and the resulting genscript is useless, just bail out.
+        assert name2src, "no source code found for %r at %r" %(name, toplevel)
     return name2src
 
 def compress_mapping(mapping):

diff -r dd157ad4f3facb7ef9d4667b83d76f8c1b5d9562 -r 339611d3748054b5a524993370cd78e112f46a58 doc/en/goodpractises.txt
--- a/doc/en/goodpractises.txt
+++ b/doc/en/goodpractises.txt
@@ -169,6 +169,14 @@
 
     python runtests.py
 
+.. note::
+
+   You must have pytest and its dependencies installed as an sdist, not
+   as wheels because genscript need the source code for generating a
+   standalone script.
+
+
+
 
 Integrating with distutils / ``python setup.py test``
 --------------------------------------------------------


https://bitbucket.org/pytest-dev/pytest/commits/8cc1f62e3798/
Changeset:   8cc1f62e3798
Branch:      pluggy1
User:        hpk42
Date:        2015-05-04 13:08:41+00:00
Summary:     adapt for current API changes of pluggy
Affected #:  1 file

diff -r 339611d3748054b5a524993370cd78e112f46a58 -r 8cc1f62e3798bc0ee9316c98bda87055d834c81a _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -133,20 +133,21 @@
             self.trace.root.setwriter(err.write)
             self.enable_tracing()
 
-    def get_hookimpl_opts(self, plugin, name):
-        method = getattr(plugin, name)
-        opts = super(PytestPluginManager, self).get_hookimpl_opts(plugin, name)
+    def parse_hookimpl_opts(self, method):
+        opts = super(PytestPluginManager, self).parse_hookimpl_opts(method)
         if opts is None:
-            if name.startswith("pytest_") and not exclude_pytest_names(name):
-                opts = {}
-                opts["tryfirst"] = hasattr(method, "tryfirst")
-                opts["trylast"] = hasattr(method, "trylast")
-                opts["optionalhook"] = hasattr(method, "optionalhook")
-                opts["hookwrapper"] = hasattr(method, "hookwrapper")
+            name = getattr(method, "__name__", None)
+            if name is not None:
+                if name.startswith("pytest_") and not exclude_pytest_names(name):
+                    opts = {}
+                    opts["tryfirst"] = hasattr(method, "tryfirst")
+                    opts["trylast"] = hasattr(method, "trylast")
+                    opts["optionalhook"] = hasattr(method, "optionalhook")
+                    opts["hookwrapper"] = hasattr(method, "hookwrapper")
         return opts
 
-    def get_hookspec_opts(self, module_or_class, name):
-        opts = super(PytestPluginManager, self).get_hookspec_opts(module_or_class, name)
+    def parse_hookspec_opts(self, module_or_class, name):
+        opts = super(PytestPluginManager, self).parse_hookspec_opts(module_or_class, name)
         if opts is None:
             if name.startswith("pytest_"):
                 meth = getattr(module_or_class, name)


https://bitbucket.org/pytest-dev/pytest/commits/b4a5e00bba50/
Changeset:   b4a5e00bba50
Branch:      pluggy1
User:        hpk42
Date:        2015-05-05 12:52:16+00:00
Summary:     merge default
Affected #:  4 files

diff -r 8cc1f62e3798bc0ee9316c98bda87055d834c81a -r b4a5e00bba500409128c658b6440f68e2fa9eb28 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -55,6 +55,10 @@
 2.7.1.dev (compared to 2.7.0)
 -----------------------------
 
+- fix issue731: do not get confused by the braces which may be present
+  and unbalanced in an object's repr while collapsing False
+  explanations.  Thanks Carl Meyer for the report and test case.
+
 - fix issue553: properly handling inspect.getsourcelines failures in
   FixtureLookupError which would lead to to an internal error,
   obfuscating the original problem. Thanks talljosh for initial

diff -r 8cc1f62e3798bc0ee9316c98bda87055d834c81a -r b4a5e00bba500409128c658b6440f68e2fa9eb28 _pytest/assertion/util.py
--- a/_pytest/assertion/util.py
+++ b/_pytest/assertion/util.py
@@ -45,13 +45,15 @@
         if where == -1:
             break
         level = 0
+        prev_c = explanation[start]
         for i, c in enumerate(explanation[start:]):
-            if c == "{":
+            if prev_c + c == "\n{":
                 level += 1
-            elif c == "}":
+            elif prev_c + c == "\n}":
                 level -= 1
                 if not level:
                     break
+            prev_c = c
         else:
             raise AssertionError("unbalanced braces: %r" % (explanation,))
         end = start + i

diff -r 8cc1f62e3798bc0ee9316c98bda87055d834c81a -r b4a5e00bba500409128c658b6440f68e2fa9eb28 _pytest/monkeypatch.py
--- a/_pytest/monkeypatch.py
+++ b/_pytest/monkeypatch.py
@@ -177,8 +177,8 @@
         sys.path.insert(0, str(path))
 
     def chdir(self, path):
-        """ Change the current working directory to the specified path
-        path can be a string or a py.path.local object
+        """ Change the current working directory to the specified path.
+        Path can be a string or a py.path.local object.
         """
         if self._cwd is None:
             self._cwd = os.getcwd()

diff -r 8cc1f62e3798bc0ee9316c98bda87055d834c81a -r b4a5e00bba500409128c658b6440f68e2fa9eb28 testing/test_assertrewrite.py
--- a/testing/test_assertrewrite.py
+++ b/testing/test_assertrewrite.py
@@ -663,3 +663,24 @@
         result.stdout.fnmatch_lines([
             "* 1 passed*",
         ])
+
+
+def test_issue731(testdir):
+    testdir.makepyfile("""
+    class LongReprWithBraces(object):
+        def __repr__(self):
+           return 'LongReprWithBraces({' + ('a' * 80) + '}' + ('a' * 120) + ')'
+
+        def some_method(self):
+            return False
+
+    def test_long_repr():
+        obj = LongReprWithBraces()
+        assert obj.some_method()
+    """)
+    result = testdir.runpytest()
+    assert 'unbalanced braces' not in result.stdout.str()
+
+
+def test_collapse_false_unbalanced_braces():
+    util._collapse_false('some text{ False\n{False = some more text\n}')


https://bitbucket.org/pytest-dev/pytest/commits/ef30f0eeae3b/
Changeset:   ef30f0eeae3b
Branch:      pluggy1
User:        hpk42
Date:        2015-05-05 19:53:04+00:00
Summary:     - some more adaptation to most recent pluggy API
- avoid using pluggin underscore api
- show pluggy version in header
Affected #:  8 files

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -9,10 +9,10 @@
 # DON't import pytest here because it causes import cycle troubles
 import sys, os
 from _pytest import hookspec # the extension point definitions
-from pluggy import PluginManager, Hookimpl, Hookspec
+from pluggy import PluginManager, HookimplDecorator, HookspecDecorator
 
-hookimpl_opts = Hookimpl("pytest")
-hookspec_opts = Hookspec("pytest")
+hookimpl_opts = HookimplDecorator("pytest")
+hookspec_opts = HookspecDecorator("pytest")
 
 # pytest startup
 #
@@ -112,7 +112,7 @@
 
 class PytestPluginManager(PluginManager):
     def __init__(self):
-        super(PytestPluginManager, self).__init__("pytest")
+        super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
         self._warnings = []
         self._conftest_plugins = set()
 
@@ -121,7 +121,7 @@
         self._conftestpath2mod = {}
         self._confcutdir = None
 
-        self.addhooks(hookspec)
+        self.add_hookspecs(hookspec)
         self.register(self)
         if os.environ.get('PYTEST_DEBUG'):
             err = sys.stderr
@@ -133,26 +133,33 @@
             self.trace.root.setwriter(err.write)
             self.enable_tracing()
 
-    def parse_hookimpl_opts(self, method):
-        opts = super(PytestPluginManager, self).parse_hookimpl_opts(method)
-        if opts is None:
-            name = getattr(method, "__name__", None)
-            if name is not None:
-                if name.startswith("pytest_") and not exclude_pytest_names(name):
-                    opts = {}
-                    opts["tryfirst"] = hasattr(method, "tryfirst")
-                    opts["trylast"] = hasattr(method, "trylast")
-                    opts["optionalhook"] = hasattr(method, "optionalhook")
-                    opts["hookwrapper"] = hasattr(method, "hookwrapper")
+    def addhooks(self, module_or_class):
+        warning = dict(code="I2",
+                       fslocation=py.code.getfslineno(sys._getframe(1)),
+                       message="use pluginmanager.add_hookspecs instead of "
+                               "deprecated addhooks() method.")
+        self._warnings.append(warning)
+        return self.add_hookspecs(module_or_class)
+
+    def parse_hookimpl_opts(self, plugin, name):
+        if exclude_pytest_names(name):
+            return None
+
+        method = getattr(plugin, name)
+        opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
+        if opts is not None:
+            for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
+                opts.setdefault(name, hasattr(method, name))
         return opts
 
     def parse_hookspec_opts(self, module_or_class, name):
-        opts = super(PytestPluginManager, self).parse_hookspec_opts(module_or_class, name)
+        opts = super(PytestPluginManager, self).parse_hookspec_opts(
+                                                module_or_class, name)
         if opts is None:
+            method = getattr(module_or_class, name)
             if name.startswith("pytest_"):
-                meth = getattr(module_or_class, name)
-                opts = {"firstresult": hasattr(meth, "firstresult"),
-                        "historic": hasattr(meth, "historic")}
+                opts = {"firstresult": hasattr(method, "firstresult"),
+                        "historic": hasattr(method, "historic")}
         return opts
 
     def _verify_hook(self, hook, hookmethod):

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -4,8 +4,6 @@
 import pkgutil
 
 import py
-import pluggy
-
 import _pytest
 
 

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -96,10 +96,10 @@
 
 def getpluginversioninfo(config):
     lines = []
-    plugininfo = config.pluginmanager._plugin_distinfo
+    plugininfo = config.pluginmanager.list_plugin_distinfo()
     if plugininfo:
         lines.append("setuptools registered plugins:")
-        for dist, plugin in plugininfo:
+        for plugin, dist in plugininfo:
             loc = getattr(plugin, '__file__', repr(plugin))
             content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
             lines.append("  " + content)
@@ -117,7 +117,7 @@
 
     if config.option.traceconfig:
         lines.append("active plugins:")
-        items = config.pluginmanager._name2plugin.items()
+        items = config.pluginmanager.list_name_plugin()
         for name, plugin in items:
             if hasattr(plugin, '__file__'):
                 r = plugin.__file__

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -1,8 +1,8 @@
 """ hook specifications for pytest plugins, invoked from main.py and builtin plugins.  """
 
-from pluggy import Hookspec
+from pluggy import HookspecDecorator
 
-hookspec_opts = Hookspec("pytest")
+hookspec_opts = HookspecDecorator("pytest")
 
 # -------------------------------------------------------------------------
 # Initialization hooks called for every plugin

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -3,6 +3,7 @@
 This is a good source for looking at the various reporting hooks.
 """
 import pytest
+import pluggy
 import py
 import sys
 import time
@@ -278,7 +279,8 @@
         if hasattr(sys, 'pypy_version_info'):
             verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
             msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
-        msg += " -- py-%s -- pytest-%s" % (py.__version__, pytest.__version__)
+        msg += ", pytest-%s, py-%s, pluggy-%s" % (
+               pytest.__version__, py.__version__, pluggy.__version__)
         if self.verbosity > 0 or self.config.option.debug or \
            getattr(self.config.option, 'pastebin', None):
             msg += " -- " + str(sys.executable)
@@ -294,10 +296,11 @@
         if config.inifile:
             inifile = config.rootdir.bestrelpath(config.inifile)
         lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
-        plugininfo = config.pluginmanager._plugin_distinfo
+
+        plugininfo = config.pluginmanager.list_plugin_distinfo()
         if plugininfo:
             l = []
-            for dist, plugin in plugininfo:
+            for plugin, dist in plugininfo:
                 name = dist.project_name
                 if name.startswith("pytest-"):
                     name = name[7:]

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 setup.py
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@
 
 
 def main():
-    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.1.0,<0.2.0']
+    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.1.0,<1.0.0']
     extras_require = {}
     if has_environment_marker_support():
         extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 testing/test_helpconfig.py
--- a/testing/test_helpconfig.py
+++ b/testing/test_helpconfig.py
@@ -7,7 +7,7 @@
     result.stderr.fnmatch_lines([
         '*pytest*%s*imported from*' % (pytest.__version__, )
     ])
-    if pytestconfig.pluginmanager._plugin_distinfo:
+    if pytestconfig.pluginmanager.list_plugin_distinfo():
         result.stderr.fnmatch_lines([
             "*setuptools registered plugins:",
             "*at*",

diff -r b4a5e00bba500409128c658b6440f68e2fa9eb28 -r ef30f0eeae3bafa76c8147382ef077099c001864 testing/test_terminal.py
--- a/testing/test_terminal.py
+++ b/testing/test_terminal.py
@@ -1,7 +1,9 @@
 """
 terminal reporting of the full testing process.
 """
-import pytest, py
+import pytest
+import py
+import pluggy
 import sys
 
 from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
@@ -408,13 +410,13 @@
         verinfo = ".".join(map(str, py.std.sys.version_info[:3]))
         result.stdout.fnmatch_lines([
             "*===== test session starts ====*",
-            "platform %s -- Python %s* -- py-%s -- pytest-%s" % (
+            "platform %s -- Python %s*pytest-%s*py-%s*pluggy-%s" % (
                 py.std.sys.platform, verinfo,
-                py.__version__, pytest.__version__),
+                pytest.__version__, py.__version__, pluggy.__version__),
             "*test_header_trailer_info.py .",
             "=* 1 passed*in *.[0-9][0-9] seconds *=",
         ])
-        if pytest.config.pluginmanager._plugin_distinfo:
+        if pytest.config.pluginmanager.list_plugin_distinfo():
             result.stdout.fnmatch_lines([
                 "plugins: *",
             ])


https://bitbucket.org/pytest-dev/pytest/commits/e3caee551cf2/
Changeset:   e3caee551cf2
Branch:      pluggy1
User:        hpk42
Date:        2015-05-05 20:29:53+00:00
Summary:     Merged default into pluggy1
Affected #:  4 files

diff -r ef30f0eeae3bafa76c8147382ef077099c001864 -r e3caee551cf2f9527046fc040a5955f134420cb2 HOWTORELEASE.rst
--- a/HOWTORELEASE.rst
+++ b/HOWTORELEASE.rst
@@ -2,11 +2,11 @@
 How to release pytest (draft)
 --------------------------------------------
 
-1. bump version numbers in setup.py and pytest/__init__.py
+1. bump version numbers in pytest/__init__.py (setup.py reads it)
 
 2. check and finalize CHANGELOG
 
-3. write doc/en/announce/pytest-VERSION.txt and include
+3. write doc/en/announce/release-VERSION.txt and include
    it in doc/en/announce/index.txt
 
 4. use devpi for uploading a release tarball to a staging area:
@@ -15,7 +15,7 @@
 
 5. run from multiple machines:
    - ``devpi use https://devpi.net/USER/dev`` 
-   - ``devpi test pytest-VERSION``
+   - ``devpi test pytest==VERSION``
 
 6. check that tests pass for relevant combinations with
    ``devpi list pytest`` 
@@ -25,14 +25,15 @@
    which is ok (tox does not support skipping on
    per-platform basis yet).
 
-7. XXX "regen docs" (not easy to do currently as it requires
-   a development version of the regendoc tool from ronny)
+7. Regenerate the docs using the toplevel makefile::
+      make docs
 
-8. go to "doc/en" and upload docs with "make install"
-   (the latter requires ssh-login permissions on pytest.org 
-   because it uses rsync).  Note that the "install" target of
-   doc/en/Makefile defines where the rsync goes to, typically
-   to the "latest" section of pytest.org.
+8. Upload the docs using the toplevel makefile::
+      make upload-docs
+   This requires ssh-login permission on pytest.org because it uses
+   rsync.
+   Note that the "install" target of doc/en/Makefile defines where the
+   rsync goes to, typically to the "latest" section of pytest.org.
 
 9. publish to pypi "devpi push pytest-2.6.2 pypi:NAME" where NAME 
    is the name of pypi.python.org as configured in your 

diff -r ef30f0eeae3bafa76c8147382ef077099c001864 -r e3caee551cf2f9527046fc040a5955f134420cb2 doc/en/announce/index.txt
--- a/doc/en/announce/index.txt
+++ b/doc/en/announce/index.txt
@@ -5,6 +5,8 @@
 .. toctree::
    :maxdepth: 2
 
+   release-2.7.1
+   release-2.7.0
    release-2.6.3
    release-2.6.2
    release-2.6.1

diff -r ef30f0eeae3bafa76c8147382ef077099c001864 -r e3caee551cf2f9527046fc040a5955f134420cb2 doc/en/announce/release-2.7.1.txt
--- /dev/null
+++ b/doc/en/announce/release-2.7.1.txt
@@ -0,0 +1,58 @@
+pytest-2.7.1: bug fixes
+=======================
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+This release is supposed to be drop-in compatible to 2.7.0.
+
+See below for the changes and see docs at:
+
+    http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+    pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+    Bruno Oliveira
+    Holger Krekel
+    Ionel Maries Cristian
+    Floris Bruynooghe
+
+Happy testing,
+The py.test Development Team
+
+
+2.7.1 (compared to 2.7.0)
+-------------------------
+
+- fix issue731: do not get confused by the braces which may be present
+  and unbalanced in an object's repr while collapsing False
+  explanations.  Thanks Carl Meyer for the report and test case.
+
+- fix issue553: properly handling inspect.getsourcelines failures in
+  FixtureLookupError which would lead to to an internal error,
+  obfuscating the original problem. Thanks talljosh for initial
+  diagnose/patch and Bruno Oliveira for final patch.
+
+- fix issue660: properly report scope-mismatch-access errors
+  independently from ordering of fixture arguments.  Also
+  avoid the pytest internal traceback which does not provide
+  information to the user. Thanks Holger Krekel.
+
+- streamlined and documented release process.  Also all versions
+  (in setup.py and documentation generation) are now read
+  from _pytest/__init__.py. Thanks Holger Krekel.
+
+- fixed docs to remove the notion that yield-fixtures are experimental.
+  They are here to stay :)  Thanks Bruno Oliveira.
+
+- Support building wheels by using environment markers for the
+  requirements.  Thanks Ionel Maries Cristian.
+
+- fixed regression to 2.6.4 which surfaced e.g. in lost stdout capture printing
+  when tests raised SystemExit. Thanks Holger Krekel.
+
+- reintroduced _pytest fixture of the pytester plugin which is used
+  at least by pytest-xdist.

diff -r ef30f0eeae3bafa76c8147382ef077099c001864 -r e3caee551cf2f9527046fc040a5955f134420cb2 doc/en/example/markers.txt
--- a/doc/en/example/markers.txt
+++ b/doc/en/example/markers.txt
@@ -55,8 +55,8 @@
     ================= 1 tests deselected by "-m 'not webtest'" =================
     ================== 3 passed, 1 deselected in 0.01 seconds ==================
 
-Selecing tests based on their node ID
--------------------------------------
+Selecting tests based on their node ID
+--------------------------------------
 
 You can provide one or more :ref:`node IDs <node-id>` as positional
 arguments to select only specified tests. This makes it easy to select


https://bitbucket.org/pytest-dev/pytest/commits/860871699d03/
Changeset:   860871699d03
Branch:      pluggy1
User:        hpk42
Date:        2015-05-06 08:08:08+00:00
Summary:     adapt to pluggy naming, rename pytest.hookspec_opts to pytest.hookspec,s ame with hookimpl_opts
Affected #:  20 files

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -27,8 +27,8 @@
   details like especially the pluginmanager.add_shutdown() API.
   Thanks Holger Krekel.
 
-- pluginmanagement: introduce ``pytest.hookimpl_opts`` and 
-  ``pytest.hookspec_opts`` decorators for setting impl/spec 
+- pluginmanagement: introduce ``pytest.hookimpl`` and 
+  ``pytest.hookspec`` decorators for setting impl/spec 
   specific parameters.  This substitutes the previous 
   now deprecated use of ``pytest.mark`` which is meant to 
   contain markers for test functions only.  

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -29,7 +29,7 @@
         help="shortcut for --capture=no.")
 
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_load_initial_conftests(early_config, parser, args):
     ns = early_config.known_args_namespace
     pluginmanager = early_config.pluginmanager
@@ -101,7 +101,7 @@
         if capfuncarg is not None:
             capfuncarg.close()
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_make_collect_report(self, collector):
         if isinstance(collector, pytest.File):
             self.resumecapture()
@@ -115,13 +115,13 @@
         else:
             yield
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_setup(self, item):
         self.resumecapture()
         yield
         self.suspendcapture_item(item, "setup")
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_call(self, item):
         self.resumecapture()
         self.activate_funcargs(item)
@@ -129,17 +129,17 @@
         #self.deactivate_funcargs() called from suspendcapture()
         self.suspendcapture_item(item, "call")
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_teardown(self, item):
         self.resumecapture()
         yield
         self.suspendcapture_item(item, "teardown")
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_keyboard_interrupt(self, excinfo):
         self.reset_capturings()
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_internalerror(self, excinfo):
         self.reset_capturings()
 

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -8,11 +8,11 @@
 import py
 # DON't import pytest here because it causes import cycle troubles
 import sys, os
-from _pytest import hookspec # the extension point definitions
-from pluggy import PluginManager, HookimplDecorator, HookspecDecorator
+import _pytest.hookspec  # the extension point definitions
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
 
-hookimpl_opts = HookimplDecorator("pytest")
-hookspec_opts = HookspecDecorator("pytest")
+hookimpl = HookimplMarker("pytest")
+hookspec = HookspecMarker("pytest")
 
 # pytest startup
 #
@@ -121,7 +121,7 @@
         self._conftestpath2mod = {}
         self._confcutdir = None
 
-        self.add_hookspecs(hookspec)
+        self.add_hookspecs(_pytest.hookspec)
         self.register(self)
         if os.environ.get('PYTEST_DEBUG'):
             err = sys.stderr
@@ -184,7 +184,7 @@
         return self.get_plugin(name)
 
     def pytest_configure(self, config):
-        # XXX now that the pluginmanager exposes hookimpl_opts(tryfirst...)
+        # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
         # we should remove tryfirst/trylast as markers
         config.addinivalue_line("markers",
             "tryfirst: mark a hook implementation function such that the "
@@ -827,7 +827,7 @@
             if not hasattr(self.option, opt.dest):
                 setattr(self.option, opt.dest, opt.default)
 
-    @hookimpl_opts(trylast=True)
+    @hookimpl(trylast=True)
     def pytest_load_initial_conftests(self, early_config):
         self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
 

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -22,7 +22,7 @@
                help="store internal tracing debug information in 'pytestdebug.log'.")
 
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_cmdline_parse():
     outcome = yield
     config = outcome.get_result()

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -1,32 +1,32 @@
 """ hook specifications for pytest plugins, invoked from main.py and builtin plugins.  """
 
-from pluggy import HookspecDecorator
+from pluggy import HookspecMarker
 
-hookspec_opts = HookspecDecorator("pytest")
+hookspec = HookspecMarker("pytest")
 
 # -------------------------------------------------------------------------
 # Initialization hooks called for every plugin
 # -------------------------------------------------------------------------
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_addhooks(pluginmanager):
     """called at plugin registration time to allow adding new hooks via a call to
-    pluginmanager.addhooks(module_or_class, prefix)."""
+    pluginmanager.add_hookspecs(module_or_class, prefix)."""
 
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_namespace():
     """return dict of name->object to be made globally available in
     the pytest namespace.  This hook is called at plugin registration
     time.
     """
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_plugin_registered(plugin, manager):
     """ a new pytest plugin got registered. """
 
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_addoption(parser):
     """register argparse-style options and ini-style config values.
 
@@ -52,7 +52,7 @@
     via (deprecated) ``pytest.config``.
     """
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_configure(config):
     """ called after command line options have been parsed
     and all plugins and initial conftest files been loaded.
@@ -65,14 +65,14 @@
 # discoverable conftest.py local plugins.
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_cmdline_parse(pluginmanager, args):
     """return initialized config object, parsing the specified args. """
 
 def pytest_cmdline_preparse(config, args):
     """(deprecated) modify command line arguments before option parsing. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_cmdline_main(config):
     """ called for performing the main command line action. The default
     implementation will invoke the configure hooks and runtest_mainloop. """
@@ -86,7 +86,7 @@
 # collection hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_collection(session):
     """ perform the collection protocol for the given session. """
 
@@ -97,14 +97,14 @@
 def pytest_collection_finish(session):
     """ called after collection has been performed and modified. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_ignore_collect(path, config):
     """ return True to prevent considering this path for collection.
     This hook is consulted for all files and directories prior to calling
     more specific hooks.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_collect_directory(path, parent):
     """ called before traversing a directory for collection files. """
 
@@ -125,7 +125,7 @@
 def pytest_deselected(items):
     """ called for test items deselected by keyword. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_make_collect_report(collector):
     """ perform ``collector.collect()`` and return a CollectReport. """
 
@@ -133,7 +133,7 @@
 # Python test function related hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pycollect_makemodule(path, parent):
     """ return a Module collector or None for the given path.
     This hook will be called for each matching test module path.
@@ -141,11 +141,11 @@
     create test modules for files that do not match as a test module.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pycollect_makeitem(collector, name, obj):
     """ return custom item/collector for a python object in a module, or None.  """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pyfunc_call(pyfuncitem):
     """ call underlying test function. """
 
@@ -156,7 +156,7 @@
 # generic runtest related hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtestloop(session):
     """ called for performing the main runtest loop
     (after collection finished). """
@@ -164,7 +164,7 @@
 def pytest_itemstart(item, node):
     """ (deprecated, use pytest_runtest_logstart). """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtest_protocol(item, nextitem):
     """ implements the runtest_setup/call/teardown protocol for
     the given test item, including capturing exceptions and calling
@@ -197,7 +197,7 @@
                    so that nextitem only needs to call setup-functions.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtest_makereport(item, call):
     """ return a :py:class:`_pytest.runner.TestReport` object
     for the given :py:class:`pytest.Item` and
@@ -242,7 +242,7 @@
 def pytest_report_header(config, startdir):
     """ return a string to be displayed as header info for terminal reporting."""
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_report_teststatus(report):
     """ return result-category, shortletter and verbose word for reporting."""
 
@@ -258,7 +258,7 @@
 # doctest hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_doctest_prepare_content(content):
     """ return processed content for a given doctest"""
 

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -512,12 +512,12 @@
     def _makeid(self):
         return ""
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_collectstart(self):
         if self.shouldstop:
             raise self.Interrupted(self.shouldstop)
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_logreport(self, report):
         if report.failed and not hasattr(report, 'wasxfail'):
             self._testsfailed += 1

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/nose.py
--- a/_pytest/nose.py
+++ b/_pytest/nose.py
@@ -24,7 +24,7 @@
         call.excinfo = call2.excinfo
 
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_runtest_setup(item):
     if is_potential_nosetest(item):
         if isinstance(item.parent, pytest.Generator):

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/pastebin.py
--- a/_pytest/pastebin.py
+++ b/_pytest/pastebin.py
@@ -11,7 +11,7 @@
         choices=['failed', 'all'],
         help="send failed|all info to bpaste.net pastebin service.")
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_configure(config):
     if config.option.pastebin == "all":
         tr = config.pluginmanager.getplugin('terminalreporter')

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/pytester.py
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -80,7 +80,7 @@
         else:
             return True
 
-    @pytest.hookimpl_opts(hookwrapper=True, tryfirst=True)
+    @pytest.hookimpl(hookwrapper=True, tryfirst=True)
     def pytest_runtest_item(self, item):
         lines1 = self.get_open_files()
         yield

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -181,7 +181,7 @@
 def pytest_sessionstart(session):
     session._fixturemanager = FixtureManager(session)
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_namespace():
     raises.Exception = pytest.fail.Exception
     return {
@@ -200,7 +200,7 @@
     return request.config
 
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_pyfunc_call(pyfuncitem):
     testfunction = pyfuncitem.obj
     if pyfuncitem._isyieldedfunction():
@@ -228,7 +228,7 @@
 def pytest_pycollect_makemodule(path, parent):
     return Module(path, parent)
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_pycollect_makeitem(collector, name, obj):
     outcome = yield
     res = outcome.get_result()

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/skipping.py
--- a/_pytest/skipping.py
+++ b/_pytest/skipping.py
@@ -133,7 +133,7 @@
         return expl
 
 
- at pytest.hookimpl_opts(tryfirst=True)
+ at pytest.hookimpl(tryfirst=True)
 def pytest_runtest_setup(item):
     evalskip = MarkEvaluator(item, 'skipif')
     if evalskip.istrue():
@@ -151,7 +151,7 @@
             if not evalxfail.get('run', True):
                 pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_makereport(item, call):
     outcome = yield
     rep = outcome.get_result()

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -268,7 +268,7 @@
     def pytest_collection_modifyitems(self):
         self.report_collect(True)
 
-    @pytest.hookimpl_opts(trylast=True)
+    @pytest.hookimpl(trylast=True)
     def pytest_sessionstart(self, session):
         self._sessionstarttime = time.time()
         if not self.showheader:
@@ -355,7 +355,7 @@
                 indent = (len(stack) - 1) * "  "
                 self._tw.line("%s%s" % (indent, col))
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_sessionfinish(self, exitstatus):
         outcome = yield
         outcome.get_result()

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -140,7 +140,7 @@
         if traceback:
             excinfo.traceback = traceback
 
- at pytest.hookimpl_opts(tryfirst=True)
+ at pytest.hookimpl(tryfirst=True)
 def pytest_runtest_makereport(item, call):
     if isinstance(item, TestCaseFunction):
         if item._excinfo:
@@ -152,7 +152,7 @@
 
 # twisted trial support
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_protocol(item):
     if isinstance(item, TestCaseFunction) and \
        'twisted.trial.unittest' in sys.modules:

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 doc/en/example/markers.txt
--- a/doc/en/example/markers.txt
+++ b/doc/en/example/markers.txt
@@ -201,9 +201,9 @@
     
     @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures 
     
-    @pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+    @pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
     
-    @pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+    @pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
     
 
 For an example on how to add and work with markers from a plugin, see
@@ -375,9 +375,9 @@
     
     @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures 
     
-    @pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+    @pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
     
-    @pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+    @pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
     
 
 Reading markers which were set from multiple places

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 doc/en/example/simple.txt
--- a/doc/en/example/simple.txt
+++ b/doc/en/example/simple.txt
@@ -534,7 +534,7 @@
     import pytest
     import os.path
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_makereport(item, call, __multicall__):
         # execute all other hooks to obtain the report object
         rep = __multicall__.execute()
@@ -607,7 +607,7 @@
 
     import pytest
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_makereport(item, call, __multicall__):
         # execute all other hooks to obtain the report object
         rep = __multicall__.execute()

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 doc/en/writing_plugins.txt
--- a/doc/en/writing_plugins.txt
+++ b/doc/en/writing_plugins.txt
@@ -292,7 +292,7 @@
 
     import pytest
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_pyfunc_call(pyfuncitem):
         # do whatever you want before the next hook executes
 
@@ -305,8 +305,7 @@
 Note that hook wrappers don't return results themselves, they merely
 perform tracing or other side effects around the actual hook implementations.
 If the result of the underlying hook is a mutable object, they may modify
-that result, however.
-
+that result but it's probably better to avoid it.
 
 
 Hook function ordering / call example
@@ -338,16 +337,24 @@
 Here is the order of execution:
 
 1. Plugin3's pytest_collection_modifyitems called until the yield point
-2. Plugin1's pytest_collection_modifyitems is called
-3. Plugin2's pytest_collection_modifyitems is called
-4. Plugin3's pytest_collection_modifyitems called for executing after the yield
-   The yield receives a :py:class:`CallOutcome` instance which encapsulates
-   the result from calling the non-wrappers.  Wrappers cannot modify the result.
+   because it is a hook wrapper.
+
+2. Plugin1's pytest_collection_modifyitems is called because it is marked
+   with ``tryfirst=True``.
+
+3. Plugin2's pytest_collection_modifyitems is called because it is marked
+   with ``trylast=True`` (but even without this mark it would come after
+   Plugin1).
+
+4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
+   point.  The yield receives a :py:class:`CallOutcome` instance which encapsulates
+   the result from calling the non-wrappers.  Wrappers shall not modify the result.
 
 It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
 ``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
 among each other.
 
+
 Declaring new hooks
 ------------------------
 
@@ -368,11 +375,11 @@
 .. _`newhooks.py`: https://bitbucket.org/pytest-dev/pytest-xdist/src/52082f70e7dd04b00361091b8af906c60fd6700f/xdist/newhooks.py?at=default
 
 
-Using hooks from 3rd party plugins
--------------------------------------
+Optionally using hooks from 3rd party plugins
+---------------------------------------------
 
 Using new hooks from plugins as explained above might be a little tricky
-because the standard :ref:`validation mechanism <validation>`:
+because of the standard :ref:`validation mechanism <validation>`:
 if you depend on a plugin that is not installed, validation will fail and
 the error message will not make much sense to your users.
 
@@ -395,7 +402,6 @@
 This has the added benefit of allowing you to conditionally install hooks
 depending on which plugins are installed.
 
-
 .. _`well specified hooks`:
 
 .. currentmodule:: _pytest.hookspec

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -13,7 +13,7 @@
 
 from _pytest.config import (
     main, UsageError, _preloadplugins, cmdline,
-    hookspec_opts, hookimpl_opts
+    hookspec, hookimpl
 )
 from _pytest import __version__
 

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -559,7 +559,7 @@
         b = testdir.mkdir("a").mkdir("b")
         b.join("conftest.py").write(py.code.Source("""
             import pytest
-            @pytest.hookimpl_opts(hookwrapper=True)
+            @pytest.hookimpl(hookwrapper=True)
             def pytest_pycollect_makeitem():
                 outcome = yield
                 if outcome.excinfo is None:

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 testing/test_helpconfig.py
--- a/testing/test_helpconfig.py
+++ b/testing/test_helpconfig.py
@@ -38,7 +38,7 @@
 def test_hookvalidation_optional(testdir):
     testdir.makeconftest("""
         import pytest
-        @pytest.hookimpl_opts(optionalhook=True)
+        @pytest.hookimpl(optionalhook=True)
         def pytest_hello(xyz):
             pass
     """)

diff -r e3caee551cf2f9527046fc040a5955f134420cb2 -r 860871699d03a09167ae732add5324224ac2bc50 testing/test_mark.py
--- a/testing/test_mark.py
+++ b/testing/test_mark.py
@@ -510,7 +510,7 @@
         """)
         testdir.makepyfile(conftest="""
             import pytest
-            @pytest.hookimpl_opts(hookwrapper=True)
+            @pytest.hookimpl(hookwrapper=True)
             def pytest_pycollect_makeitem(name):
                 outcome = yield
                 if name == "TestClass":


https://bitbucket.org/pytest-dev/pytest/commits/16fa917e78e4/
Changeset:   16fa917e78e4
Branch:      pluggy1
User:        hpk42
Date:        2015-05-06 12:58:46+00:00
Summary:     - make sure sub pytest runs use the same basetemp
- depend on pluggy < 0.3
Affected #:  3 files

diff -r 860871699d03a09167ae732add5324224ac2bc50 -r 16fa917e78e49532647702b6350f1755371aafe9 _pytest/__init__.py
--- a/_pytest/__init__.py
+++ b/_pytest/__init__.py
@@ -1,2 +1,2 @@
 #
-__version__ = '2.8.0.dev2'
+__version__ = '2.8.0.dev3'

diff -r 860871699d03a09167ae732add5324224ac2bc50 -r 16fa917e78e49532647702b6350f1755371aafe9 _pytest/pytester.py
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -712,8 +712,20 @@
         option "--runpytest" and return a :py:class:`RunResult`.
 
         """
+        args = self._ensure_basetemp(args)
         return self._runpytest_method(*args, **kwargs)
 
+    def _ensure_basetemp(self, args):
+        args = [str(x) for x in args]
+        for x in args:
+            if str(x).startswith('--basetemp'):
+                #print ("basedtemp exists: %s" %(args,))
+                break
+        else:
+            args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
+            #print ("added basetemp: %s" %(args,))
+        return args
+
     def parseconfig(self, *args):
         """Return a new py.test Config instance from given commandline args.
 
@@ -726,12 +738,8 @@
         modules which will be registered with the PluginManager.
 
         """
-        args = [str(x) for x in args]
-        for x in args:
-            if str(x).startswith('--basetemp'):
-                break
-        else:
-            args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
+        args = self._ensure_basetemp(args)
+
         import _pytest.config
         config = _pytest.config._prepareconfig(args, self.plugins)
         # we don't know what the test will do with this half-setup config

diff -r 860871699d03a09167ae732add5324224ac2bc50 -r 16fa917e78e49532647702b6350f1755371aafe9 setup.py
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@
 
 
 def main():
-    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.1.0,<1.0.0']
+    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.2.0,<0.3.0']
     extras_require = {}
     if has_environment_marker_support():
         extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']


https://bitbucket.org/pytest-dev/pytest/commits/af98ffcfb0fb/
Changeset:   af98ffcfb0fb
User:        hpk42
Date:        2015-05-06 13:02:05+00:00
Summary:     Merged in hpk42/pytest-patches/pluggy1 (pull request #290)

integrate pluggy as external plugin manager
Affected #:  29 files

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -27,8 +27,8 @@
   details like especially the pluginmanager.add_shutdown() API.
   Thanks Holger Krekel.
 
-- pluginmanagement: introduce ``pytest.hookimpl_opts`` and 
-  ``pytest.hookspec_opts`` decorators for setting impl/spec 
+- pluginmanagement: introduce ``pytest.hookimpl`` and 
+  ``pytest.hookspec`` decorators for setting impl/spec 
   specific parameters.  This substitutes the previous 
   now deprecated use of ``pytest.mark`` which is meant to 
   contain markers for test functions only.  

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/__init__.py
--- a/_pytest/__init__.py
+++ b/_pytest/__init__.py
@@ -1,2 +1,2 @@
 #
-__version__ = '2.8.0.dev2'
+__version__ = '2.8.0.dev3'

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -29,7 +29,7 @@
         help="shortcut for --capture=no.")
 
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_load_initial_conftests(early_config, parser, args):
     ns = early_config.known_args_namespace
     pluginmanager = early_config.pluginmanager
@@ -101,7 +101,7 @@
         if capfuncarg is not None:
             capfuncarg.close()
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_make_collect_report(self, collector):
         if isinstance(collector, pytest.File):
             self.resumecapture()
@@ -115,13 +115,13 @@
         else:
             yield
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_setup(self, item):
         self.resumecapture()
         yield
         self.suspendcapture_item(item, "setup")
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_call(self, item):
         self.resumecapture()
         self.activate_funcargs(item)
@@ -129,17 +129,17 @@
         #self.deactivate_funcargs() called from suspendcapture()
         self.suspendcapture_item(item, "call")
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_teardown(self, item):
         self.resumecapture()
         yield
         self.suspendcapture_item(item, "teardown")
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_keyboard_interrupt(self, excinfo):
         self.reset_capturings()
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_internalerror(self, excinfo):
         self.reset_capturings()
 

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -8,8 +8,11 @@
 import py
 # DON't import pytest here because it causes import cycle troubles
 import sys, os
-from _pytest import hookspec # the extension point definitions
-from _pytest.core import PluginManager, hookimpl_opts, varnames
+import _pytest.hookspec  # the extension point definitions
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
+
+hookimpl = HookimplMarker("pytest")
+hookspec = HookspecMarker("pytest")
 
 # pytest startup
 #
@@ -106,10 +109,10 @@
            name.startswith("pytest_funcarg__")
 
 
+
 class PytestPluginManager(PluginManager):
     def __init__(self):
-        super(PytestPluginManager, self).__init__(prefix="pytest_",
-                                                  excludefunc=exclude_pytest_names)
+        super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
         self._warnings = []
         self._conftest_plugins = set()
 
@@ -118,7 +121,7 @@
         self._conftestpath2mod = {}
         self._confcutdir = None
 
-        self.addhooks(hookspec)
+        self.add_hookspecs(_pytest.hookspec)
         self.register(self)
         if os.environ.get('PYTEST_DEBUG'):
             err = sys.stderr
@@ -130,12 +133,39 @@
             self.trace.root.setwriter(err.write)
             self.enable_tracing()
 
+    def addhooks(self, module_or_class):
+        warning = dict(code="I2",
+                       fslocation=py.code.getfslineno(sys._getframe(1)),
+                       message="use pluginmanager.add_hookspecs instead of "
+                               "deprecated addhooks() method.")
+        self._warnings.append(warning)
+        return self.add_hookspecs(module_or_class)
 
-    def _verify_hook(self, hook, plugin):
-        super(PytestPluginManager, self)._verify_hook(hook, plugin)
-        method = getattr(plugin, hook.name)
-        if "__multicall__" in varnames(method):
-            fslineno = py.code.getfslineno(method)
+    def parse_hookimpl_opts(self, plugin, name):
+        if exclude_pytest_names(name):
+            return None
+
+        method = getattr(plugin, name)
+        opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
+        if opts is not None:
+            for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
+                opts.setdefault(name, hasattr(method, name))
+        return opts
+
+    def parse_hookspec_opts(self, module_or_class, name):
+        opts = super(PytestPluginManager, self).parse_hookspec_opts(
+                                                module_or_class, name)
+        if opts is None:
+            method = getattr(module_or_class, name)
+            if name.startswith("pytest_"):
+                opts = {"firstresult": hasattr(method, "firstresult"),
+                        "historic": hasattr(method, "historic")}
+        return opts
+
+    def _verify_hook(self, hook, hookmethod):
+        super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
+        if "__multicall__" in hookmethod.argnames:
+            fslineno = py.code.getfslineno(hookmethod.function)
             warning = dict(code="I1",
                            fslocation=fslineno,
                            message="%r hook uses deprecated __multicall__ "
@@ -154,7 +184,7 @@
         return self.get_plugin(name)
 
     def pytest_configure(self, config):
-        # XXX now that the pluginmanager exposes hookimpl_opts(tryfirst...)
+        # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
         # we should remove tryfirst/trylast as markers
         config.addinivalue_line("markers",
             "tryfirst: mark a hook implementation function such that the "
@@ -797,7 +827,7 @@
             if not hasattr(self.option, opt.dest):
                 setattr(self.option, opt.dest, opt.default)
 
-    @hookimpl_opts(trylast=True)
+    @hookimpl(trylast=True)
     def pytest_load_initial_conftests(self, early_config):
         self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
 

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/core.py
--- a/_pytest/core.py
+++ /dev/null
@@ -1,590 +0,0 @@
-"""
-PluginManager, basic initialization and tracing.
-"""
-import sys
-from inspect import isfunction, ismethod, isclass, formatargspec, getargspec
-import py
-
-py3 = sys.version_info > (3,0)
-
-def hookspec_opts(firstresult=False, historic=False):
-    """ returns a decorator which will define a function as a hook specfication.
-
-    If firstresult is True the 1:N hook call (N being the number of registered
-    hook implementation functions) will stop at I<=N when the I'th function
-    returns a non-None result.
-
-    If historic is True calls to a hook will be memorized and replayed
-    on later registered plugins.
-    """
-    def setattr_hookspec_opts(func):
-        if historic and firstresult:
-            raise ValueError("cannot have a historic firstresult hook")
-        if firstresult:
-            func.firstresult = firstresult
-        if historic:
-            func.historic = historic
-        return func
-    return setattr_hookspec_opts
-
-
-def hookimpl_opts(hookwrapper=False, optionalhook=False,
-                  tryfirst=False, trylast=False):
-    """ Return a decorator which marks a function as a hook implementation.
-
-    If optionalhook is True a missing matching hook specification will not result
-    in an error (by default it is an error if no matching spec is found).
-
-    If tryfirst is True this hook implementation will run as early as possible
-    in the chain of N hook implementations for a specfication.
-
-    If trylast is True this hook implementation will run as late as possible
-    in the chain of N hook implementations.
-
-    If hookwrapper is True the hook implementations needs to execute exactly
-    one "yield".  The code before the yield is run early before any non-hookwrapper
-    function is run.  The code after the yield is run after all non-hookwrapper
-    function have run.  The yield receives an ``CallOutcome`` object representing
-    the exception or result outcome of the inner calls (including other hookwrapper
-    calls).
-    """
-    def setattr_hookimpl_opts(func):
-        if hookwrapper:
-            func.hookwrapper = True
-        if optionalhook:
-            func.optionalhook = True
-        if tryfirst:
-            func.tryfirst = True
-        if trylast:
-            func.trylast = True
-        return func
-    return setattr_hookimpl_opts
-
-
-class TagTracer:
-    def __init__(self):
-        self._tag2proc = {}
-        self.writer = None
-        self.indent = 0
-
-    def get(self, name):
-        return TagTracerSub(self, (name,))
-
-    def format_message(self, tags, args):
-        if isinstance(args[-1], dict):
-            extra = args[-1]
-            args = args[:-1]
-        else:
-            extra = {}
-
-        content = " ".join(map(str, args))
-        indent = "  " * self.indent
-
-        lines = [
-            "%s%s [%s]\n" %(indent, content, ":".join(tags))
-        ]
-
-        for name, value in extra.items():
-            lines.append("%s    %s: %s\n" % (indent, name, value))
-        return lines
-
-    def processmessage(self, tags, args):
-        if self.writer is not None and args:
-            lines = self.format_message(tags, args)
-            self.writer(''.join(lines))
-        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,))
-
-
-def raise_wrapfail(wrap_controller, msg):
-    co = wrap_controller.gi_code
-    raise RuntimeError("wrap_controller at %r %s:%d %s" %
-                   (co.co_name, co.co_filename, co.co_firstlineno, msg))
-
-
-def wrapped_call(wrap_controller, func):
-    """ Wrap calling to a function with a generator which needs to yield
-    exactly once.  The yield point will trigger calling the wrapped function
-    and return its CallOutcome to the yield point.  The generator then needs
-    to finish (raise StopIteration) in order for the wrapped call to complete.
-    """
-    try:
-        next(wrap_controller)   # first yield
-    except StopIteration:
-        raise_wrapfail(wrap_controller, "did not yield")
-    call_outcome = CallOutcome(func)
-    try:
-        wrap_controller.send(call_outcome)
-        raise_wrapfail(wrap_controller, "has second yield")
-    except StopIteration:
-        pass
-    return call_outcome.get_result()
-
-
-class CallOutcome:
-    """ Outcome of a function call, either an exception or a proper result.
-    Calling the ``get_result`` method will return the result or reraise
-    the exception raised when the function was called. """
-    excinfo = None
-    def __init__(self, func):
-        try:
-            self.result = func()
-        except BaseException:
-            self.excinfo = sys.exc_info()
-
-    def force_result(self, result):
-        self.result = result
-        self.excinfo = None
-
-    def get_result(self):
-        if self.excinfo is None:
-            return self.result
-        else:
-            ex = self.excinfo
-            if py3:
-                raise ex[1].with_traceback(ex[2])
-            py.builtin._reraise(*ex)
-
-
-class TracedHookExecution:
-    def __init__(self, pluginmanager, before, after):
-        self.pluginmanager = pluginmanager
-        self.before = before
-        self.after = after
-        self.oldcall = pluginmanager._inner_hookexec
-        assert not isinstance(self.oldcall, TracedHookExecution)
-        self.pluginmanager._inner_hookexec = self
-
-    def __call__(self, hook, methods, kwargs):
-        self.before(hook, methods, kwargs)
-        outcome = CallOutcome(lambda: self.oldcall(hook, methods, kwargs))
-        self.after(outcome, hook, methods, kwargs)
-        return outcome.get_result()
-
-    def undo(self):
-        self.pluginmanager._inner_hookexec = self.oldcall
-
-
-class PluginManager(object):
-    """ Core Pluginmanager class which manages registration
-    of plugin objects and 1:N hook calling.
-
-    You can register new hooks by calling ``addhooks(module_or_class)``.
-    You can register plugin objects (which contain hooks) by calling
-    ``register(plugin)``.  The Pluginmanager is initialized with a
-    prefix that is searched for in the names of the dict of registered
-    plugin objects.  An optional excludefunc allows to blacklist names which
-    are not considered as hooks despite a matching prefix.
-
-    For debugging purposes you can call ``enable_tracing()``
-    which will subsequently send debug information to the trace helper.
-    """
-
-    def __init__(self, prefix, excludefunc=None):
-        self._prefix = prefix
-        self._excludefunc = excludefunc
-        self._name2plugin = {}
-        self._plugin2hookcallers = {}
-        self._plugin_distinfo = []
-        self.trace = TagTracer().get("pluginmanage")
-        self.hook = HookRelay(self.trace.root.get("hook"))
-        self._inner_hookexec = lambda hook, methods, kwargs: \
-                               MultiCall(methods, kwargs, hook.firstresult).execute()
-
-    def _hookexec(self, hook, methods, kwargs):
-        # called from all hookcaller instances.
-        # enable_tracing will set its own wrapping function at self._inner_hookexec
-        return self._inner_hookexec(hook, methods, kwargs)
-
-    def enable_tracing(self):
-        """ enable tracing of hook calls and return an undo function. """
-        hooktrace = self.hook._trace
-
-        def before(hook, methods, kwargs):
-            hooktrace.root.indent += 1
-            hooktrace(hook.name, kwargs)
-
-        def after(outcome, hook, methods, kwargs):
-            if outcome.excinfo is None:
-                hooktrace("finish", hook.name, "-->", outcome.result)
-            hooktrace.root.indent -= 1
-
-        return TracedHookExecution(self, before, after).undo
-
-    def subset_hook_caller(self, name, remove_plugins):
-        """ Return a new HookCaller instance for the named method
-        which manages calls to all registered plugins except the
-        ones from remove_plugins. """
-        orig = getattr(self.hook, name)
-        plugins_to_remove = [plugin for plugin in remove_plugins
-                                    if hasattr(plugin, name)]
-        if plugins_to_remove:
-            hc = HookCaller(orig.name, orig._hookexec, orig._specmodule_or_class)
-            for plugin in orig._plugins:
-                if plugin not in plugins_to_remove:
-                    hc._add_plugin(plugin)
-                    # we also keep track of this hook caller so it
-                    # gets properly removed on plugin unregistration
-                    self._plugin2hookcallers.setdefault(plugin, []).append(hc)
-            return hc
-        return orig
-
-    def register(self, plugin, name=None):
-        """ Register a plugin and return its canonical name or None if the name
-        is blocked from registering.  Raise a ValueError if the plugin is already
-        registered. """
-        plugin_name = name or self.get_canonical_name(plugin)
-
-        if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
-            if self._name2plugin.get(plugin_name, -1) is None:
-                return  # blocked plugin, return None to indicate no registration
-            raise ValueError("Plugin already registered: %s=%s\n%s" %(
-                              plugin_name, plugin, self._name2plugin))
-
-        self._name2plugin[plugin_name] = plugin
-
-        # register prefix-matching hook specs of the plugin
-        self._plugin2hookcallers[plugin] = hookcallers = []
-        for name in dir(plugin):
-            if name.startswith(self._prefix):
-                hook = getattr(self.hook, name, None)
-                if hook is None:
-                    if self._excludefunc is not None and self._excludefunc(name):
-                        continue
-                    hook = HookCaller(name, self._hookexec)
-                    setattr(self.hook, name, hook)
-                elif hook.has_spec():
-                    self._verify_hook(hook, plugin)
-                    hook._maybe_apply_history(getattr(plugin, name))
-                hookcallers.append(hook)
-                hook._add_plugin(plugin)
-        return plugin_name
-
-    def unregister(self, plugin=None, name=None):
-        """ unregister a plugin object and all its contained hook implementations
-        from internal data structures. """
-        if name is None:
-            assert plugin is not None, "one of name or plugin needs to be specified"
-            name = self.get_name(plugin)
-
-        if plugin is None:
-            plugin = self.get_plugin(name)
-
-        # if self._name2plugin[name] == None registration was blocked: ignore
-        if self._name2plugin.get(name):
-            del self._name2plugin[name]
-
-        for hookcaller in self._plugin2hookcallers.pop(plugin, []):
-            hookcaller._remove_plugin(plugin)
-
-        return plugin
-
-    def set_blocked(self, name):
-        """ block registrations of the given name, unregister if already registered. """
-        self.unregister(name=name)
-        self._name2plugin[name] = None
-
-    def addhooks(self, module_or_class):
-        """ add new hook definitions from the given module_or_class using
-        the prefix/excludefunc with which the PluginManager was initialized. """
-        names = []
-        for name in dir(module_or_class):
-            if name.startswith(self._prefix):
-                hc = getattr(self.hook, name, None)
-                if hc is None:
-                    hc = HookCaller(name, self._hookexec, module_or_class)
-                    setattr(self.hook, name, hc)
-                else:
-                    # plugins registered this hook without knowing the spec
-                    hc.set_specification(module_or_class)
-                    for plugin in hc._plugins:
-                        self._verify_hook(hc, plugin)
-                names.append(name)
-
-        if not names:
-            raise ValueError("did not find new %r hooks in %r"
-                             %(self._prefix, module_or_class))
-
-    def get_plugins(self):
-        """ return the set of registered plugins. """
-        return set(self._plugin2hookcallers)
-
-    def is_registered(self, plugin):
-        """ Return True if the plugin is already registered. """
-        return plugin in self._plugin2hookcallers
-
-    def get_canonical_name(self, plugin):
-        """ Return canonical name for a plugin object. Note that a plugin
-        may be registered under a different name which was specified
-        by the caller of register(plugin, name). To obtain the name
-        of an registered plugin use ``get_name(plugin)`` instead."""
-        return getattr(plugin, "__name__", None) or str(id(plugin))
-
-    def get_plugin(self, name):
-        """ Return a plugin or None for the given name. """
-        return self._name2plugin.get(name)
-
-    def get_name(self, plugin):
-        """ Return name for registered plugin or None if not registered. """
-        for name, val in self._name2plugin.items():
-            if plugin == val:
-                return name
-
-    def _verify_hook(self, hook, plugin):
-        method = getattr(plugin, hook.name)
-        pluginname = self.get_name(plugin)
-
-        if hook.is_historic() and hasattr(method, "hookwrapper"):
-            raise PluginValidationError(
-                "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper" %(
-                 pluginname, hook.name))
-
-        for arg in varnames(method):
-            if arg not in hook.argnames:
-                raise PluginValidationError(
-                    "Plugin %r\nhook %r\nargument %r not available\n"
-                     "plugin definition: %s\n"
-                     "available hookargs: %s" %(
-                     pluginname, hook.name, arg, formatdef(method),
-                       ", ".join(hook.argnames)))
-
-    def check_pending(self):
-        """ Verify that all hooks which have not been verified against
-        a hook specification are optional, otherwise raise PluginValidationError"""
-        for name in self.hook.__dict__:
-            if name.startswith(self._prefix):
-                hook = getattr(self.hook, name)
-                if not hook.has_spec():
-                    for plugin in hook._plugins:
-                        method = getattr(plugin, hook.name)
-                        if not getattr(method, "optionalhook", False):
-                            raise PluginValidationError(
-                                "unknown hook %r in plugin %r" %(name, plugin))
-
-    def load_setuptools_entrypoints(self, entrypoint_name):
-        """ Load modules from querying the specified setuptools entrypoint name.
-        Return the number of loaded plugins. """
-        from pkg_resources import iter_entry_points, DistributionNotFound
-        for ep in iter_entry_points(entrypoint_name):
-            # is the plugin registered or blocked?
-            if self.get_plugin(ep.name) or ep.name in self._name2plugin:
-                continue
-            try:
-                plugin = ep.load()
-            except DistributionNotFound:
-                continue
-            self.register(plugin, name=ep.name)
-            self._plugin_distinfo.append((ep.dist, plugin))
-        return len(self._plugin_distinfo)
-
-
-class MultiCall:
-    """ execute a call into multiple python functions/methods. """
-
-    # XXX note that the __multicall__ argument is supported only
-    # for pytest compatibility reasons.  It was never officially
-    # supported there and is explicitely deprecated since 2.8
-    # so we can remove it soon, allowing to avoid the below recursion
-    # in execute() and simplify/speed up the execute loop.
-
-    def __init__(self, methods, kwargs, firstresult=False):
-        self.methods = methods
-        self.kwargs = kwargs
-        self.kwargs["__multicall__"] = self
-        self.firstresult = firstresult
-
-    def execute(self):
-        all_kwargs = self.kwargs
-        self.results = results = []
-        firstresult = self.firstresult
-
-        while self.methods:
-            method = self.methods.pop()
-            args = [all_kwargs[argname] for argname in varnames(method)]
-            if hasattr(method, "hookwrapper"):
-                return wrapped_call(method(*args), self.execute)
-            res = method(*args)
-            if res is not None:
-                if firstresult:
-                    return res
-                results.append(res)
-
-        if not firstresult:
-            return results
-
-    def __repr__(self):
-        status = "%d meths" % (len(self.methods),)
-        if hasattr(self, "results"):
-            status = ("%d results, " % len(self.results)) + status
-        return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
-
-
-
-def varnames(func, startindex=None):
-    """ return argument name tuple for a function, method, class or callable.
-
-    In case of a class, its "__init__" method is considered.
-    For methods the "self" parameter is not included unless you are passing
-    an unbound method with Python3 (which has no supports for unbound methods)
-    """
-    cache = getattr(func, "__dict__", {})
-    try:
-        return cache["_varnames"]
-    except KeyError:
-        pass
-    if isclass(func):
-        try:
-            func = func.__init__
-        except AttributeError:
-            return ()
-        startindex = 1
-    else:
-        if not isfunction(func) and not ismethod(func):
-            func = getattr(func, '__call__', func)
-        if startindex is None:
-            startindex = int(ismethod(func))
-
-    rawcode = py.code.getrawcode(func)
-    try:
-        x = rawcode.co_varnames[startindex:rawcode.co_argcount]
-    except AttributeError:
-        x = ()
-    else:
-        defaults = func.__defaults__
-        if defaults:
-            x = x[:-len(defaults)]
-    try:
-        cache["_varnames"] = x
-    except TypeError:
-        pass
-    return x
-
-
-class HookRelay:
-    def __init__(self, trace):
-        self._trace = trace
-
-
-class HookCaller(object):
-    def __init__(self, name, hook_execute, specmodule_or_class=None):
-        self.name = name
-        self._plugins = []
-        self._wrappers = []
-        self._nonwrappers = []
-        self._hookexec = hook_execute
-        if specmodule_or_class is not None:
-            self.set_specification(specmodule_or_class)
-
-    def has_spec(self):
-        return hasattr(self, "_specmodule_or_class")
-
-    def set_specification(self, specmodule_or_class):
-        assert not self.has_spec()
-        self._specmodule_or_class = specmodule_or_class
-        specfunc = getattr(specmodule_or_class, self.name)
-        argnames = varnames(specfunc, startindex=isclass(specmodule_or_class))
-        assert "self" not in argnames  # sanity check
-        self.argnames = ["__multicall__"] + list(argnames)
-        self.firstresult = getattr(specfunc, 'firstresult', False)
-        if hasattr(specfunc, "historic"):
-            self._call_history = []
-
-    def is_historic(self):
-        return hasattr(self, "_call_history")
-
-    def _remove_plugin(self, plugin):
-        self._plugins.remove(plugin)
-        meth = getattr(plugin, self.name)
-        try:
-            self._nonwrappers.remove(meth)
-        except ValueError:
-            self._wrappers.remove(meth)
-
-    def _add_plugin(self, plugin):
-        self._plugins.append(plugin)
-        self._add_method(getattr(plugin, self.name))
-
-    def _add_method(self, meth):
-        if hasattr(meth, 'hookwrapper'):
-            methods = self._wrappers
-        else:
-            methods = self._nonwrappers
-
-        if hasattr(meth, 'trylast'):
-            methods.insert(0, meth)
-        elif hasattr(meth, 'tryfirst'):
-            methods.append(meth)
-        else:
-            # find last non-tryfirst method
-            i = len(methods) - 1
-            while i >= 0 and hasattr(methods[i], "tryfirst"):
-                i -= 1
-            methods.insert(i + 1, meth)
-
-    def __repr__(self):
-        return "<HookCaller %r>" %(self.name,)
-
-    def __call__(self, **kwargs):
-        assert not self.is_historic()
-        return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
-    def call_historic(self, proc=None, kwargs=None):
-        self._call_history.append((kwargs or {}, proc))
-        # historizing hooks don't return results
-        self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
-    def call_extra(self, methods, kwargs):
-        """ Call the hook with some additional temporarily participating
-        methods using the specified kwargs as call parameters. """
-        old = list(self._nonwrappers), list(self._wrappers)
-        for method in methods:
-            self._add_method(method)
-        try:
-            return self(**kwargs)
-        finally:
-            self._nonwrappers, self._wrappers = old
-
-    def _maybe_apply_history(self, method):
-        if self.is_historic():
-            for kwargs, proc in self._call_history:
-                res = self._hookexec(self, [method], kwargs)
-                if res and proc is not None:
-                    proc(res[0])
-
-
-class PluginValidationError(Exception):
-    """ plugin failed validation. """
-
-
-def formatdef(func):
-    return "%s%s" % (
-        func.__name__,
-        formatargspec(*getargspec(func))
-    )

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -4,7 +4,6 @@
 import pkgutil
 
 import py
-
 import _pytest
 
 
@@ -33,6 +32,9 @@
         for pyfile in toplevel.visit('*.py'):
             pkg = pkgname(name, toplevel, pyfile)
             name2src[pkg] = pyfile.read()
+        # with wheels py source code might be not be installed
+        # and the resulting genscript is useless, just bail out.
+        assert name2src, "no source code found for %r at %r" %(name, toplevel)
     return name2src
 
 def compress_mapping(mapping):
@@ -69,7 +71,7 @@
     genscript = config.getvalue("genscript")
     if genscript:
         tw = py.io.TerminalWriter()
-        deps =  ['py', '_pytest', 'pytest']
+        deps =  ['py', 'pluggy', '_pytest', 'pytest']
         if sys.version_info < (2,7):
             deps.append("argparse")
             tw.line("generated script will run on python2.6-python3.3++")

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -22,7 +22,7 @@
                help="store internal tracing debug information in 'pytestdebug.log'.")
 
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_cmdline_parse():
     outcome = yield
     config = outcome.get_result()
@@ -96,10 +96,10 @@
 
 def getpluginversioninfo(config):
     lines = []
-    plugininfo = config.pluginmanager._plugin_distinfo
+    plugininfo = config.pluginmanager.list_plugin_distinfo()
     if plugininfo:
         lines.append("setuptools registered plugins:")
-        for dist, plugin in plugininfo:
+        for plugin, dist in plugininfo:
             loc = getattr(plugin, '__file__', repr(plugin))
             content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
             lines.append("  " + content)
@@ -117,7 +117,7 @@
 
     if config.option.traceconfig:
         lines.append("active plugins:")
-        items = config.pluginmanager._name2plugin.items()
+        items = config.pluginmanager.list_name_plugin()
         for name, plugin in items:
             if hasattr(plugin, '__file__'):
                 r = plugin.__file__

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -1,30 +1,32 @@
 """ hook specifications for pytest plugins, invoked from main.py and builtin plugins.  """
 
-from _pytest.core import hookspec_opts
+from pluggy import HookspecMarker
+
+hookspec = HookspecMarker("pytest")
 
 # -------------------------------------------------------------------------
 # Initialization hooks called for every plugin
 # -------------------------------------------------------------------------
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_addhooks(pluginmanager):
     """called at plugin registration time to allow adding new hooks via a call to
-    pluginmanager.addhooks(module_or_class, prefix)."""
+    pluginmanager.add_hookspecs(module_or_class, prefix)."""
 
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_namespace():
     """return dict of name->object to be made globally available in
     the pytest namespace.  This hook is called at plugin registration
     time.
     """
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_plugin_registered(plugin, manager):
     """ a new pytest plugin got registered. """
 
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_addoption(parser):
     """register argparse-style options and ini-style config values.
 
@@ -50,7 +52,7 @@
     via (deprecated) ``pytest.config``.
     """
 
- at hookspec_opts(historic=True)
+ at hookspec(historic=True)
 def pytest_configure(config):
     """ called after command line options have been parsed
     and all plugins and initial conftest files been loaded.
@@ -63,14 +65,14 @@
 # discoverable conftest.py local plugins.
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_cmdline_parse(pluginmanager, args):
     """return initialized config object, parsing the specified args. """
 
 def pytest_cmdline_preparse(config, args):
     """(deprecated) modify command line arguments before option parsing. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_cmdline_main(config):
     """ called for performing the main command line action. The default
     implementation will invoke the configure hooks and runtest_mainloop. """
@@ -84,7 +86,7 @@
 # collection hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_collection(session):
     """ perform the collection protocol for the given session. """
 
@@ -95,14 +97,14 @@
 def pytest_collection_finish(session):
     """ called after collection has been performed and modified. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_ignore_collect(path, config):
     """ return True to prevent considering this path for collection.
     This hook is consulted for all files and directories prior to calling
     more specific hooks.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_collect_directory(path, parent):
     """ called before traversing a directory for collection files. """
 
@@ -123,7 +125,7 @@
 def pytest_deselected(items):
     """ called for test items deselected by keyword. """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_make_collect_report(collector):
     """ perform ``collector.collect()`` and return a CollectReport. """
 
@@ -131,7 +133,7 @@
 # Python test function related hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pycollect_makemodule(path, parent):
     """ return a Module collector or None for the given path.
     This hook will be called for each matching test module path.
@@ -139,11 +141,11 @@
     create test modules for files that do not match as a test module.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pycollect_makeitem(collector, name, obj):
     """ return custom item/collector for a python object in a module, or None.  """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_pyfunc_call(pyfuncitem):
     """ call underlying test function. """
 
@@ -154,7 +156,7 @@
 # generic runtest related hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtestloop(session):
     """ called for performing the main runtest loop
     (after collection finished). """
@@ -162,7 +164,7 @@
 def pytest_itemstart(item, node):
     """ (deprecated, use pytest_runtest_logstart). """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtest_protocol(item, nextitem):
     """ implements the runtest_setup/call/teardown protocol for
     the given test item, including capturing exceptions and calling
@@ -195,7 +197,7 @@
                    so that nextitem only needs to call setup-functions.
     """
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_runtest_makereport(item, call):
     """ return a :py:class:`_pytest.runner.TestReport` object
     for the given :py:class:`pytest.Item` and
@@ -240,7 +242,7 @@
 def pytest_report_header(config, startdir):
     """ return a string to be displayed as header info for terminal reporting."""
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_report_teststatus(report):
     """ return result-category, shortletter and verbose word for reporting."""
 
@@ -256,7 +258,7 @@
 # doctest hooks
 # -------------------------------------------------------------------------
 
- at hookspec_opts(firstresult=True)
+ at hookspec(firstresult=True)
 def pytest_doctest_prepare_content(content):
     """ return processed content for a given doctest"""
 

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -501,23 +501,23 @@
     def __init__(self, config):
         FSCollector.__init__(self, config.rootdir, parent=None,
                              config=config, session=self)
-        self.config.pluginmanager.register(self, name="session")
+        self._fs2hookproxy = {}
         self._testsfailed = 0
         self.shouldstop = False
         self.trace = config.trace.root.get("collection")
         self._norecursepatterns = config.getini("norecursedirs")
         self.startdir = py.path.local()
-        self._fs2hookproxy = {}
+        self.config.pluginmanager.register(self, name="session")
 
     def _makeid(self):
         return ""
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_collectstart(self):
         if self.shouldstop:
             raise self.Interrupted(self.shouldstop)
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_logreport(self, report):
         if report.failed and not hasattr(report, 'wasxfail'):
             self._testsfailed += 1

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/nose.py
--- a/_pytest/nose.py
+++ b/_pytest/nose.py
@@ -24,7 +24,7 @@
         call.excinfo = call2.excinfo
 
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_runtest_setup(item):
     if is_potential_nosetest(item):
         if isinstance(item.parent, pytest.Generator):

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/pastebin.py
--- a/_pytest/pastebin.py
+++ b/_pytest/pastebin.py
@@ -11,7 +11,7 @@
         choices=['failed', 'all'],
         help="send failed|all info to bpaste.net pastebin service.")
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_configure(config):
     if config.option.pastebin == "all":
         tr = config.pluginmanager.getplugin('terminalreporter')

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/pytester.py
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -13,7 +13,7 @@
 import py
 import pytest
 from py.builtin import print_
-from _pytest.core import TracedHookExecution
+from pluggy import _TracedHookExecution
 
 from _pytest.main import Session, EXIT_OK
 
@@ -80,7 +80,7 @@
         else:
             return True
 
-    @pytest.hookimpl_opts(hookwrapper=True, tryfirst=True)
+    @pytest.hookimpl(hookwrapper=True, tryfirst=True)
     def pytest_runtest_item(self, item):
         lines1 = self.get_open_files()
         yield
@@ -198,7 +198,7 @@
             self.calls.append(ParsedCall(hook.name, kwargs))
         def after(outcome, hook, method, kwargs):
             pass
-        executor = TracedHookExecution(pluginmanager, before, after)
+        executor = _TracedHookExecution(pluginmanager, before, after)
         self._undo_wrapping = executor.undo
 
     def finish_recording(self):
@@ -712,8 +712,20 @@
         option "--runpytest" and return a :py:class:`RunResult`.
 
         """
+        args = self._ensure_basetemp(args)
         return self._runpytest_method(*args, **kwargs)
 
+    def _ensure_basetemp(self, args):
+        args = [str(x) for x in args]
+        for x in args:
+            if str(x).startswith('--basetemp'):
+                #print ("basedtemp exists: %s" %(args,))
+                break
+        else:
+            args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
+            #print ("added basetemp: %s" %(args,))
+        return args
+
     def parseconfig(self, *args):
         """Return a new py.test Config instance from given commandline args.
 
@@ -726,12 +738,8 @@
         modules which will be registered with the PluginManager.
 
         """
-        args = [str(x) for x in args]
-        for x in args:
-            if str(x).startswith('--basetemp'):
-                break
-        else:
-            args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
+        args = self._ensure_basetemp(args)
+
         import _pytest.config
         config = _pytest.config._prepareconfig(args, self.plugins)
         # we don't know what the test will do with this half-setup config

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -8,7 +8,11 @@
 from py._code.code import TerminalRepr
 
 import _pytest
-cutdir = py.path.local(_pytest.__file__).dirpath()
+import pluggy
+
+cutdir2 = py.path.local(_pytest.__file__).dirpath()
+cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
+
 
 NoneType = type(None)
 NOTSET = object()
@@ -18,6 +22,11 @@
 # used to work around a python2 exception info leak
 exc_clear = getattr(sys, 'exc_clear', lambda: None)
 
+
+def filter_traceback(entry):
+    return entry.path != cutdir1 and not entry.path.relto(cutdir2)
+
+
 def getfslineno(obj):
     # xxx let decorators etc specify a sane ordering
     while hasattr(obj, "__wrapped__"):
@@ -172,7 +181,7 @@
 def pytest_sessionstart(session):
     session._fixturemanager = FixtureManager(session)
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_namespace():
     raises.Exception = pytest.fail.Exception
     return {
@@ -191,7 +200,7 @@
     return request.config
 
 
- at pytest.hookimpl_opts(trylast=True)
+ at pytest.hookimpl(trylast=True)
 def pytest_pyfunc_call(pyfuncitem):
     testfunction = pyfuncitem.obj
     if pyfuncitem._isyieldedfunction():
@@ -219,7 +228,7 @@
 def pytest_pycollect_makemodule(path, parent):
     return Module(path, parent)
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_pycollect_makeitem(collector, name, obj):
     outcome = yield
     res = outcome.get_result()
@@ -604,7 +613,11 @@
             if ntraceback == traceback:
                 ntraceback = ntraceback.cut(path=path)
                 if ntraceback == traceback:
-                    ntraceback = ntraceback.cut(excludepath=cutdir)
+                    #ntraceback = ntraceback.cut(excludepath=cutdir2)
+                    ntraceback = ntraceback.filter(filter_traceback)
+                    if not ntraceback:
+                        ntraceback = traceback
+
             excinfo.traceback = ntraceback.filter()
             # issue364: mark all but first and last frames to
             # only show a single-line message for each frame

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/skipping.py
--- a/_pytest/skipping.py
+++ b/_pytest/skipping.py
@@ -133,7 +133,7 @@
         return expl
 
 
- at pytest.hookimpl_opts(tryfirst=True)
+ at pytest.hookimpl(tryfirst=True)
 def pytest_runtest_setup(item):
     evalskip = MarkEvaluator(item, 'skipif')
     if evalskip.istrue():
@@ -151,7 +151,7 @@
             if not evalxfail.get('run', True):
                 pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_makereport(item, call):
     outcome = yield
     rep = outcome.get_result()

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -3,6 +3,7 @@
 This is a good source for looking at the various reporting hooks.
 """
 import pytest
+import pluggy
 import py
 import sys
 import time
@@ -267,7 +268,7 @@
     def pytest_collection_modifyitems(self):
         self.report_collect(True)
 
-    @pytest.hookimpl_opts(trylast=True)
+    @pytest.hookimpl(trylast=True)
     def pytest_sessionstart(self, session):
         self._sessionstarttime = time.time()
         if not self.showheader:
@@ -278,7 +279,8 @@
         if hasattr(sys, 'pypy_version_info'):
             verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
             msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
-        msg += " -- py-%s -- pytest-%s" % (py.__version__, pytest.__version__)
+        msg += ", pytest-%s, py-%s, pluggy-%s" % (
+               pytest.__version__, py.__version__, pluggy.__version__)
         if self.verbosity > 0 or self.config.option.debug or \
            getattr(self.config.option, 'pastebin', None):
             msg += " -- " + str(sys.executable)
@@ -294,10 +296,11 @@
         if config.inifile:
             inifile = config.rootdir.bestrelpath(config.inifile)
         lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
-        plugininfo = config.pluginmanager._plugin_distinfo
+
+        plugininfo = config.pluginmanager.list_plugin_distinfo()
         if plugininfo:
             l = []
-            for dist, plugin in plugininfo:
+            for plugin, dist in plugininfo:
                 name = dist.project_name
                 if name.startswith("pytest-"):
                     name = name[7:]
@@ -352,7 +355,7 @@
                 indent = (len(stack) - 1) * "  "
                 self._tw.line("%s%s" % (indent, col))
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_sessionfinish(self, exitstatus):
         outcome = yield
         outcome.get_result()

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -140,7 +140,7 @@
         if traceback:
             excinfo.traceback = traceback
 
- at pytest.hookimpl_opts(tryfirst=True)
+ at pytest.hookimpl(tryfirst=True)
 def pytest_runtest_makereport(item, call):
     if isinstance(item, TestCaseFunction):
         if item._excinfo:
@@ -152,7 +152,7 @@
 
 # twisted trial support
 
- at pytest.hookimpl_opts(hookwrapper=True)
+ at pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_protocol(item):
     if isinstance(item, TestCaseFunction) and \
        'twisted.trial.unittest' in sys.modules:

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 doc/en/example/markers.txt
--- a/doc/en/example/markers.txt
+++ b/doc/en/example/markers.txt
@@ -201,9 +201,9 @@
     
     @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures 
     
-    @pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+    @pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
     
-    @pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+    @pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
     
 
 For an example on how to add and work with markers from a plugin, see
@@ -375,9 +375,9 @@
     
     @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures 
     
-    @pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+    @pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
     
-    @pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+    @pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
     
 
 Reading markers which were set from multiple places

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 doc/en/example/simple.txt
--- a/doc/en/example/simple.txt
+++ b/doc/en/example/simple.txt
@@ -534,7 +534,7 @@
     import pytest
     import os.path
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_makereport(item, call, __multicall__):
         # execute all other hooks to obtain the report object
         rep = __multicall__.execute()
@@ -607,7 +607,7 @@
 
     import pytest
 
-    @pytest.hookimpl_opts(tryfirst=True)
+    @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_makereport(item, call, __multicall__):
         # execute all other hooks to obtain the report object
         rep = __multicall__.execute()

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 doc/en/goodpractises.txt
--- a/doc/en/goodpractises.txt
+++ b/doc/en/goodpractises.txt
@@ -169,6 +169,14 @@
 
     python runtests.py
 
+.. note::
+
+   You must have pytest and its dependencies installed as an sdist, not
+   as wheels because genscript need the source code for generating a
+   standalone script.
+
+
+
 
 Integrating with distutils / ``python setup.py test``
 --------------------------------------------------------

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 doc/en/writing_plugins.txt
--- a/doc/en/writing_plugins.txt
+++ b/doc/en/writing_plugins.txt
@@ -292,7 +292,7 @@
 
     import pytest
 
-    @pytest.hookimpl_opts(hookwrapper=True)
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_pyfunc_call(pyfuncitem):
         # do whatever you want before the next hook executes
 
@@ -305,8 +305,7 @@
 Note that hook wrappers don't return results themselves, they merely
 perform tracing or other side effects around the actual hook implementations.
 If the result of the underlying hook is a mutable object, they may modify
-that result, however.
-
+that result but it's probably better to avoid it.
 
 
 Hook function ordering / call example
@@ -338,16 +337,24 @@
 Here is the order of execution:
 
 1. Plugin3's pytest_collection_modifyitems called until the yield point
-2. Plugin1's pytest_collection_modifyitems is called
-3. Plugin2's pytest_collection_modifyitems is called
-4. Plugin3's pytest_collection_modifyitems called for executing after the yield
-   The yield receives a :py:class:`CallOutcome` instance which encapsulates
-   the result from calling the non-wrappers.  Wrappers cannot modify the result.
+   because it is a hook wrapper.
+
+2. Plugin1's pytest_collection_modifyitems is called because it is marked
+   with ``tryfirst=True``.
+
+3. Plugin2's pytest_collection_modifyitems is called because it is marked
+   with ``trylast=True`` (but even without this mark it would come after
+   Plugin1).
+
+4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
+   point.  The yield receives a :py:class:`CallOutcome` instance which encapsulates
+   the result from calling the non-wrappers.  Wrappers shall not modify the result.
 
 It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
 ``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
 among each other.
 
+
 Declaring new hooks
 ------------------------
 
@@ -368,11 +375,11 @@
 .. _`newhooks.py`: https://bitbucket.org/pytest-dev/pytest-xdist/src/52082f70e7dd04b00361091b8af906c60fd6700f/xdist/newhooks.py?at=default
 
 
-Using hooks from 3rd party plugins
--------------------------------------
+Optionally using hooks from 3rd party plugins
+---------------------------------------------
 
 Using new hooks from plugins as explained above might be a little tricky
-because the standard :ref:`validation mechanism <validation>`:
+because of the standard :ref:`validation mechanism <validation>`:
 if you depend on a plugin that is not installed, validation will fail and
 the error message will not make much sense to your users.
 
@@ -395,7 +402,6 @@
 This has the added benefit of allowing you to conditionally install hooks
 depending on which plugins are installed.
 
-
 .. _`well specified hooks`:
 
 .. currentmodule:: _pytest.hookspec

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -11,8 +11,10 @@
 
 # else we are imported
 
-from _pytest.config import main, UsageError, _preloadplugins, cmdline
-from _pytest.core import hookspec_opts, hookimpl_opts
+from _pytest.config import (
+    main, UsageError, _preloadplugins, cmdline,
+    hookspec, hookimpl
+)
 from _pytest import __version__
 
 _preloadplugins() # to populate pytest.* namespace so help(pytest) works

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 setup.py
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@
 
 
 def main():
-    install_requires = ['py>=1.4.27.dev2']
+    install_requires = ['py>=1.4.27.dev2', 'pluggy>=0.2.0,<0.3.0']
     extras_require = {}
     if has_environment_marker_support():
         extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -559,7 +559,7 @@
         b = testdir.mkdir("a").mkdir("b")
         b.join("conftest.py").write(py.code.Source("""
             import pytest
-            @pytest.hookimpl_opts(hookwrapper=True)
+            @pytest.hookimpl(hookwrapper=True)
             def pytest_pycollect_makeitem():
                 outcome = yield
                 if outcome.excinfo is None:

diff -r e14e1c5215fc9f2022ba103c52929f9ddfb15d81 -r af98ffcfb0fb1d0116bfc4b46c49dfa31fdfc7c2 testing/test_config.py
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -357,9 +357,9 @@
     pm.register(m)
     hc = pm.hook.pytest_load_initial_conftests
     l = hc._nonwrappers + hc._wrappers
-    assert l[-1].__module__ == "_pytest.capture"
-    assert l[-2] == m.pytest_load_initial_conftests
-    assert l[-3].__module__ == "_pytest.config"
+    assert l[-1].function.__module__ == "_pytest.capture"
+    assert l[-2].function == m.pytest_load_initial_conftests
+    assert l[-3].function.__module__ == "_pytest.config"
 
 class TestWarning:
     def test_warn_config(self, testdir):

This diff is so big that we needed to truncate the remainder.

Repository URL: https://bitbucket.org/pytest-dev/pytest/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.


More information about the pytest-commit mailing list