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