introduce plugin discovery through setuptools "pytest11" entrypoints
and refine execnet dependency handling. Prepare 1.1 release --HG-- branch : trunk
This commit is contained in:
parent
0e03ae1ee8
commit
ed03eef81b
|
@ -1,7 +1,10 @@
|
||||||
Changes between 1.1.1 and 1.1.0
|
Changes between 1.1.1 and 1.1.0
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4 (required)
|
- introduce automatic lookup of 'pytest11' entrypoints
|
||||||
|
via setuptools' pkg_resources.iter_entry_points
|
||||||
|
|
||||||
|
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||||
|
|
||||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,16 @@ class VirtualEnv(object):
|
||||||
] + list(args),
|
] + list(args),
|
||||||
**kw)
|
**kw)
|
||||||
|
|
||||||
|
def pytest_getouterr(self, *args):
|
||||||
|
self.ensure()
|
||||||
|
args = [self._cmd("python"), self._cmd("py.test")] + list(args)
|
||||||
|
popen = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
out, err = popen.communicate()
|
||||||
|
return out
|
||||||
|
|
||||||
|
def setup_develop(self):
|
||||||
|
self.ensure()
|
||||||
|
return self.pcall("python", "setup.py", "develop")
|
||||||
|
|
||||||
def easy_install(self, *packages, **kw):
|
def easy_install(self, *packages, **kw):
|
||||||
args = []
|
args = []
|
||||||
|
@ -110,4 +120,25 @@ def test_make_sdist_and_run_it(py_setup, venv):
|
||||||
ch = gw.remote_exec("import py ; channel.send(py.__version__)")
|
ch = gw.remote_exec("import py ; channel.send(py.__version__)")
|
||||||
version = ch.receive()
|
version = ch.receive()
|
||||||
assert version == py.__version__
|
assert version == py.__version__
|
||||||
ch = gw.remote_exec("import py ; channel.send(py.__version__)")
|
|
||||||
|
def test_plugin_setuptools_entry_point_integration(py_setup, venv, tmpdir):
|
||||||
|
sdist = py_setup.make_sdist(venv.path)
|
||||||
|
venv.easy_install(str(sdist))
|
||||||
|
# create a sample plugin
|
||||||
|
basedir = tmpdir.mkdir("testplugin")
|
||||||
|
basedir.join("setup.py").write("""if 1:
|
||||||
|
from setuptools import setup
|
||||||
|
setup(name="testplugin",
|
||||||
|
entry_points = {'pytest11': ['testplugin=tp1']},
|
||||||
|
py_modules = ['tp1'],
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
basedir.join("tp1.py").write(py.code.Source("""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption("--testpluginopt", action="store_true")
|
||||||
|
"""))
|
||||||
|
basedir.chdir()
|
||||||
|
print ("created sample plugin in %s" %basedir)
|
||||||
|
venv.setup_develop()
|
||||||
|
out = venv.pytest_getouterr("-h")
|
||||||
|
assert "testpluginopt" in out
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
py.test/pylib 1.1.1: bugfix release, improved 1.0.x backward compat
|
py.test/pylib 1.1.1: bugfix release, setuptools plugin registration
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
This is a compatibility fixing release of pylib/py.test to work
|
This is a compatibility fixing release of pylib/py.test to work
|
||||||
better with previous 1.0.x code bases. It also contains fixes
|
better with previous 1.0.x test code bases. It also contains fixes
|
||||||
and changes to work with `execnet>=1.0.0b4`_ which is now required
|
and changes to work with `execnet>=1.0.0b4`_. 1.1.1 also introduces
|
||||||
(but is not installed automatically, issue "easy_install -U execnet").
|
a new mechanism for registering plugins via setuptools.
|
||||||
Last but not least, documentation has been improved.
|
Last but not least, documentation has been improved.
|
||||||
|
|
||||||
What is pylib/py.test?
|
What is pylib/py.test?
|
||||||
|
@ -17,7 +17,7 @@ existing common Python test suites without modification. Moreover,
|
||||||
it offers some unique features not found in other
|
it offers some unique features not found in other
|
||||||
testing tools. See http://pytest.org for more info.
|
testing tools. See http://pytest.org for more info.
|
||||||
|
|
||||||
The pylib contains a localpath and svnpath implementation
|
The pylib also contains a localpath and svnpath implementation
|
||||||
and some developer-oriented command line tools. See
|
and some developer-oriented command line tools. See
|
||||||
http://pylib.org for more info.
|
http://pylib.org for more info.
|
||||||
|
|
||||||
|
@ -31,7 +31,10 @@ holger (http://twitter.com/hpk42)
|
||||||
Changes between 1.1.1 and 1.1.0
|
Changes between 1.1.1 and 1.1.0
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4 (required)
|
- introduce automatic lookup of 'pytest11' entrypoints
|
||||||
|
via setuptools' pkg_resources.iter_entry_points
|
||||||
|
|
||||||
|
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||||
|
|
||||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,8 @@ Plugin discovery at tool startup
|
||||||
|
|
||||||
py.test loads plugin modules at tool startup in the following way:
|
py.test loads plugin modules at tool startup in the following way:
|
||||||
|
|
||||||
|
* by loading all plugins registered through `setuptools entry points`_.
|
||||||
|
|
||||||
* by reading the ``PYTEST_PLUGINS`` environment variable
|
* by reading the ``PYTEST_PLUGINS`` environment variable
|
||||||
and importing the comma-separated list of named plugins.
|
and importing the comma-separated list of named plugins.
|
||||||
|
|
||||||
|
@ -132,17 +134,13 @@ py.test loads plugin modules at tool startup in the following way:
|
||||||
and loading the specified plugin before actual command line parsing.
|
and loading the specified plugin before actual command line parsing.
|
||||||
|
|
||||||
* by loading all `conftest.py plugin`_ files as inferred by the command line
|
* by loading all `conftest.py plugin`_ files as inferred by the command line
|
||||||
invocation
|
invocation (test files and all of its parent directories).
|
||||||
|
Note that ``conftest.py`` files from sub directories are loaded
|
||||||
|
during test collection and not at tool startup.
|
||||||
|
|
||||||
* by recursively loading all plugins specified by the
|
* by recursively loading all plugins specified by the
|
||||||
``pytest_plugins`` variable in a ``conftest.py`` file
|
``pytest_plugins`` variable in a ``conftest.py`` file
|
||||||
|
|
||||||
Note that at tool startup only ``conftest.py`` files in
|
|
||||||
the directory of the specified test modules (or the current dir if None)
|
|
||||||
or any of the parent directories are found. There is no try to
|
|
||||||
pre-scan all subdirectories to find ``conftest.py`` files or test
|
|
||||||
modules.
|
|
||||||
|
|
||||||
Specifying plugins in a test module or plugin
|
Specifying plugins in a test module or plugin
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
@ -160,8 +158,8 @@ must be lowercase.
|
||||||
.. _`conftest.py plugin`:
|
.. _`conftest.py plugin`:
|
||||||
.. _`conftestplugin`:
|
.. _`conftestplugin`:
|
||||||
|
|
||||||
conftest.py as anonymous per-project plugins
|
Writing per-project plugins (conftest.py)
|
||||||
--------------------------------------------------
|
------------------------------------------------------
|
||||||
|
|
||||||
The purpose of ``conftest.py`` files is to allow `project-specific
|
The purpose of ``conftest.py`` files is to allow `project-specific
|
||||||
test configuration`_. They thus make for a good place to implement
|
test configuration`_. They thus make for a good place to implement
|
||||||
|
@ -181,6 +179,55 @@ by defining the following hook in a ``conftest.py`` file:
|
||||||
if config.getvalue("runall"):
|
if config.getvalue("runall"):
|
||||||
collect_ignore[:] = []
|
collect_ignore[:] = []
|
||||||
|
|
||||||
|
.. _`setuptools entry points`:
|
||||||
|
|
||||||
|
Writing setuptools-registered plugins
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
.. _`Distribute`: http://pypi.python.org/pypi/distribute
|
||||||
|
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
||||||
|
|
||||||
|
If you want to make your plugin publically available, you
|
||||||
|
can use `setuptools`_ or `Distribute`_ which both allow
|
||||||
|
to register an entry point. ``py.test`` will register
|
||||||
|
all objects with the ``pytest11`` entry point.
|
||||||
|
To make your plugin available you may insert the following
|
||||||
|
lines in your setuptools/distribute-based setup-invocation:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
# sample ./setup.py file
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="myproject",
|
||||||
|
packages = ['myproject']
|
||||||
|
|
||||||
|
# the following makes a plugin available to py.test
|
||||||
|
entry_points = {
|
||||||
|
'pytest11': [
|
||||||
|
'name_of_plugin = myproject.pluginmodule',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
If a package is installed with this setup, py.test will load
|
||||||
|
``myproject.pluginmodule`` under the ``name_of_plugin`` name
|
||||||
|
and use it as a plugin.
|
||||||
|
|
||||||
|
Accessing another plugin by name
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
If a plugin wants to collaborate with code from
|
||||||
|
another plugin it can obtain a reference through
|
||||||
|
the plugin manager like this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
||||||
|
|
||||||
|
If you want to look at the names of existing plugins, use
|
||||||
|
the ``--traceconfig`` option.
|
||||||
|
|
||||||
.. _`well specified hooks`:
|
.. _`well specified hooks`:
|
||||||
.. _`implement hooks`:
|
.. _`implement hooks`:
|
||||||
|
|
|
@ -74,6 +74,7 @@ class Config(object):
|
||||||
|
|
||||||
def _preparse(self, args):
|
def _preparse(self, args):
|
||||||
self._conftest.setinitial(args)
|
self._conftest.setinitial(args)
|
||||||
|
self.pluginmanager.consider_setuptools_entrypoints()
|
||||||
self.pluginmanager.consider_preparse(args)
|
self.pluginmanager.consider_preparse(args)
|
||||||
self.pluginmanager.consider_env()
|
self.pluginmanager.consider_env()
|
||||||
self.pluginmanager.do_addoption(self._parser)
|
self.pluginmanager.do_addoption(self._parser)
|
||||||
|
|
|
@ -77,6 +77,14 @@ class PluginManager(object):
|
||||||
for spec in self._envlist("PYTEST_PLUGINS"):
|
for spec in self._envlist("PYTEST_PLUGINS"):
|
||||||
self.import_plugin(spec)
|
self.import_plugin(spec)
|
||||||
|
|
||||||
|
def consider_setuptools_entrypoints(self):
|
||||||
|
from pkg_resources import iter_entry_points
|
||||||
|
for ep in iter_entry_points('pytest11'):
|
||||||
|
if ep.name in self._name2plugin:
|
||||||
|
continue
|
||||||
|
plugin = ep.load()
|
||||||
|
self.register(plugin, name=ep.name)
|
||||||
|
|
||||||
def consider_preparse(self, args):
|
def consider_preparse(self, args):
|
||||||
for opt1,opt2 in zip(args, args[1:]):
|
for opt1,opt2 in zip(args, args[1:]):
|
||||||
if opt1 == "-p":
|
if opt1 == "-p":
|
||||||
|
|
|
@ -42,6 +42,24 @@ class TestBootstrapping:
|
||||||
l3 = len(pluginmanager.getplugins())
|
l3 = len(pluginmanager.getplugins())
|
||||||
assert l2 == l3
|
assert l2 == l3
|
||||||
|
|
||||||
|
def test_consider_setuptools_instantiation(self, monkeypatch):
|
||||||
|
pkg_resources = py.test.importorskip("pkg_resources")
|
||||||
|
def my_iter(name):
|
||||||
|
assert name == "pytest11"
|
||||||
|
class EntryPoint:
|
||||||
|
name = "mytestplugin"
|
||||||
|
def load(self):
|
||||||
|
class PseudoPlugin:
|
||||||
|
x = 42
|
||||||
|
return PseudoPlugin()
|
||||||
|
return iter([EntryPoint()])
|
||||||
|
|
||||||
|
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
||||||
|
pluginmanager = PluginManager()
|
||||||
|
pluginmanager.consider_setuptools_entrypoints()
|
||||||
|
plugin = pluginmanager.getplugin("mytestplugin")
|
||||||
|
assert plugin.x == 42
|
||||||
|
|
||||||
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
|
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
|
||||||
x500 = testdir.makepyfile(pytest_x500="#")
|
x500 = testdir.makepyfile(pytest_x500="#")
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
|
|
Loading…
Reference in New Issue