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
|
||||
=====================================
|
||||
|
||||
- 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
|
||||
|
||||
|
|
|
@ -88,6 +88,16 @@ class VirtualEnv(object):
|
|||
] + list(args),
|
||||
**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):
|
||||
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__)")
|
||||
version = ch.receive()
|
||||
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
|
||||
better with previous 1.0.x code bases. It also contains fixes
|
||||
and changes to work with `execnet>=1.0.0b4`_ which is now required
|
||||
(but is not installed automatically, issue "easy_install -U execnet").
|
||||
better with previous 1.0.x test code bases. It also contains fixes
|
||||
and changes to work with `execnet>=1.0.0b4`_. 1.1.1 also introduces
|
||||
a new mechanism for registering plugins via setuptools.
|
||||
Last but not least, documentation has been improved.
|
||||
|
||||
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
|
||||
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
|
||||
http://pylib.org for more info.
|
||||
|
||||
|
@ -31,7 +31,10 @@ holger (http://twitter.com/hpk42)
|
|||
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
|
||||
|
||||
|
|
|
@ -125,6 +125,8 @@ Plugin discovery at tool startup
|
|||
|
||||
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
|
||||
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.
|
||||
|
||||
* 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
|
||||
``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
|
||||
-----------------------------------------------
|
||||
|
||||
|
@ -160,8 +158,8 @@ must be lowercase.
|
|||
.. _`conftest.py plugin`:
|
||||
.. _`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
|
||||
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"):
|
||||
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`:
|
||||
.. _`implement hooks`:
|
||||
|
|
|
@ -74,6 +74,7 @@ class Config(object):
|
|||
|
||||
def _preparse(self, args):
|
||||
self._conftest.setinitial(args)
|
||||
self.pluginmanager.consider_setuptools_entrypoints()
|
||||
self.pluginmanager.consider_preparse(args)
|
||||
self.pluginmanager.consider_env()
|
||||
self.pluginmanager.do_addoption(self._parser)
|
||||
|
|
|
@ -77,6 +77,14 @@ class PluginManager(object):
|
|||
for spec in self._envlist("PYTEST_PLUGINS"):
|
||||
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):
|
||||
for opt1,opt2 in zip(args, args[1:]):
|
||||
if opt1 == "-p":
|
||||
|
|
|
@ -42,6 +42,24 @@ class TestBootstrapping:
|
|||
l3 = len(pluginmanager.getplugins())
|
||||
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):
|
||||
x500 = testdir.makepyfile(pytest_x500="#")
|
||||
p = testdir.makepyfile("""
|
||||
|
|
Loading…
Reference in New Issue