* add a script for generating plugin docs

* improve generation of docs
* refine plugins docs

--HG--
branch : 1.0.x
This commit is contained in:
holger krekel 2009-07-14 21:17:13 +02:00
parent cd0972ede8
commit 013381fae1
14 changed files with 340 additions and 163 deletions

View File

@ -1,7 +1,7 @@
Changes between 1.0.0b7 and 1.0.0b8
=====================================
* workaround a logging module interaction ("closing already closed
* workaround a buggy logging module interaction ("closing already closed
files"). Thanks to Sridhar Ratnakumar for triggering.
* if plugins use "py.test.importorskip" for importing
@ -12,7 +12,9 @@ Changes between 1.0.0b7 and 1.0.0b8
- refined funcargs doc , use the term "factory" instead
of "provider"
- added a new talk/tutorial doc page
- better download page
- better plugin docstrings
- added new plugins page and automatic doc generation script
* fixed teardown problem related to partially failing funcarg setups
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now

View File

@ -173,20 +173,20 @@ class Project:
stylesheet=stylesheet, encoding=encoding)
content = strip_html_header(content, encoding=encoding)
page = self.Page(self, "[%s] " % txtpath.purebasename,
title = "[%s] %s" % (txtpath.purebasename, py.version)
page = self.Page(self, title,
outputpath, stylesheeturl=stylesheet)
try:
svninfo = txtpath.info()
modified = " modified %s by %s" % (worded_time(svninfo.mtime),
getrealname(svninfo.last_author))
except (KeyboardInterrupt, SystemExit):
raise
except:
modified = py.process.cmdexec(
"hg tip --template 'last modified {date|shortdate}'"
)
except py.process.cmdexec.Error:
modified = " "
page.contentspace.append(
html.div(html.div(modified, style="float: right; font-style: italic;"),
html.div(html.div(modified,
style="float: right; font-style: italic;"),
id = 'docinfoline'))
page.contentspace.append(py.xml.raw(content))

View File

@ -6,12 +6,11 @@ Downloading
Latest Release, see `PyPI project page`_
"easy_install py"
using setuptools / easy_install
===================================================
With a working `setuptools installation`_ you can type::
easy_install -U py
to get the latest release of the py lib. The ``-U`` switch
@ -26,21 +25,77 @@ install the full py lib. If you don't have a compiler available
you can still install the py lib but without greenlets - look
below for the ``install_lib`` target.
**IMPORTANT NOTE**: if you are using Windows and have previous
installations of the py lib on your system, please download
**IMPORTANT NOTE**: if you are using Windows and have
0.8 versions of the py lib on your system, please download
and execute http://codespeak.net/svn/py/build/winpathclean.py
This will check that no previous files are getting in the way.
(Unfortunately we don't know about a way to execute this
code automatically during the above install).
You can find out the py lib version with::
Installing on Debian or Fedora
import py
print py.version
.. _`checkout`:
Installing from version control / develop mode
=================================================
To follow development or help with fixing things
for the next release, checkout the complete code
and documentation source with mercurial_::
hg clone https://bitbucket.org/hpk42/py-trunk/
With a working `setuptools installation`_ you can then issue::
python setup.py develop
in order to work with your checkout version.
.. _mercurial: http://mercurial.selenic.com/wiki/
Working without setuptools / from source
==========================================
If you have a checkout_ or a tarball_ it is actually not neccessary to issue
``setup.py`` commands in order to use py lib and its tools. You can
simply add the root directory to ``PYTHONPATH`` and ``py/bin`` or
``py\bin\win32`` to your ``PATH`` settings.
There are also helper scripts to set the environment
on windows::
c:\\path\to\checkout\py\env.cmd
and on linux/osx you can add something like this to
your shell initialization::
eval `python ~/path/to/checkout/py/env.py`
both of which which will get you good settings
for ``PYTHONPATH`` and ``PATH``.
Note also that the command line scripts will look
for "nearby" py libs, so if you have a layout like this::
mypkg/
subpkg1/
tests/
tests/
py/
then issuing ``py.test subpkg1`` will use the py lib
from that projects root directory.
Debian and RPM packages
===================================
As of July 2009 pytest/pylib 1.0 RPMs and Debian packages
are not yet available.
are not yet available. So you will only find older
versions.
On Debian systems look for ``python-codespeak-lib``.
*This package is probably outdated - if somebody
*But this package is probably outdated - if somebody
can help with bringing this up to date,
that would be very much appreciated.*
@ -50,12 +105,14 @@ Dwayne Bailey has thankfully put together a Fedora `RPM`_.
.. _`setuptools installation`: http://pypi.python.org/pypi/setuptools
Downloading a tar/zip archive and installing that
.. _tarball:
Installing from a TAR archive
===================================================
You need a working `setuptools installation`_.
Go to the python package index (pypi) and download a tar or zip file:
Go to the python package index (pypi) and download a tar file:
http://pypi.python.org/pypi/py/
@ -63,52 +120,3 @@ and unpack it to a directory, where you then type::
python setup.py install
Installing from subversion / develop mode
============================================
To follow development or help with fixing things
for the next release, checkout the complete code
and documentation source with mercurial::
hg clone https://bitbucket.org/hpk42/py-trunk/
or with subversion:
svn co http://codespeak.net/svn/py/trunk
With a working `setuptools installation`_ you can then issue::
python setup.py develop
in order to work with your checkout version.
Working with multiple py lib versions / svn externals
=======================================================
If you happen to have multiple versions of the py lib
around or you ship the py lib as an svn-external to
then you might want to use py lib scripts more directly.
For example if you have a project layout like this::
mypkg/
subpkg1/
tests/
tests/
py/ # as svn-external, could be specific tag/version
then you want to make sure that the actual local py lib is used
and not another system-wide version. For this you need to add
``py/bin`` or ``py\bin\win32`` respectively to your system's PATH settings.
You can do this by executing (on windows) a script to set the environment::
c:\\path\to\checkout\py\env.cmd
or on linux/osx you can add something like this to your shell
initialization::
eval `python ~/path/to/checkout/py/env.py`
to get good settings for PYTHONPATH and PATH.

View File

@ -39,27 +39,3 @@ Minor support functionality
.. _`py.xml`: xml.html
.. _`miscellaneous features`: misc.html
Full Contents
===================================
.. toctree::
:maxdepth: 2
test
execnet
path
code
bin
xml
io
log
misc
coding-style
contact
download
releases
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -4,15 +4,18 @@ Extending and customizing py.test
.. _`local plugin`:
py.test implements much of its functionality by calling `well specified
hooks`_. Python modules which contain such hook functions are called
plugins. Hook functions are discovered in ``conftest.py`` files or
in **named** plugins. ``conftest.py`` files are sometimes called "anonymous"
or conftest plugins. They are useful for keeping test extensions close
to the application package. Named plugins are normal python modules or packages
that can be distributed separately. Named plugins need to follow a naming pattern;
they have an all lowercase ``pytest_`` prefixed name. While conftest plugins are
discovered automatically, named plugins must be explicitely specified.
py.test implements much of its functionality by calling `well specified
hooks`_. Python modules which contain such hook functions are called
plugins. Hook functions are discovered in ``conftest.py`` files or in
`named plugins`_. ``conftest.py`` files are sometimes called
"anonymous" or conftest plugins. They are useful for keeping test
extensions close to your application. Named plugins are normal python
modules or packages that can be distributed separately. Named plugins
need to follow a naming pattern; they have an all lowercase ``pytest_``
prefixed name. While conftest plugins are discovered automatically,
named plugins must be explicitely specified.
.. _`named plugins`: plugins.html
.. _`tool startup`:
.. _`test tool starts up`:

View File

@ -292,7 +292,8 @@ To make it easier to distinguish the generated tests it is possible to specify a
easy to extend
=========================================
Since 1.0 py.test has advanced `extension mechanisms`_.
Since 1.0 py.test has advanced `extension mechanisms`_
and a growing `list of plugins`_.
One can can easily modify or add aspects for for
purposes such as:
@ -301,6 +302,7 @@ purposes such as:
* running non-python tests
* managing custom test state setup
.. _`list of plugins`: plugins.html
.. _`extension mechanisms`: extend.html
.. _`reStructured Text`: http://docutils.sourceforge.net

72
doc/test/plugins.txt Normal file
View File

@ -0,0 +1,72 @@
=====================================================
Plugins related to Python test functions and programs
=====================================================
* `pytest_xfail`_ mark tests as expected-to-fail and report them separately.
* `pytest_figleaf`_ write and report coverage data with 'figleaf'.
* `pytest_monkeypatch`_ safely patch object attributes, dicts and environment variables.
* `pytest_iocapture`_ 'capsys' and 'capfd' funcargs for capturing stdout/stderror.
* `pytest_recwarn`_ helpers for asserting deprecation and other warnings.
==============================================
Plugins for other testing styles and languages
==============================================
* `pytest_unittest`_ automatically discover and run traditional "unittest.py" style tests.
* `pytest_doctest`_ collect and execute doctests from modules and test files.
* `pytest_restdoc`_ perform ReST syntax, local and remote reference tests on .rst/.txt files.
* `pytest_oejskit`_ Testing Javascript in real browsers
=================================================
Plugins for generic reporting and failure logging
=================================================
* `pytest_pocoo`_ submit failure information to paste.pocoo.org
* `pytest_resultlog`_ resultlog plugin for machine-readable logging of test results.
* `pytest_terminal`_ terminal reporting of the full testing process.
=====================================
internal plugins / core functionality
=====================================
* `pytest_pdb`_ interactive debugging with the Python Debugger.
* `pytest_keyword`_ py.test.mark / keyword plugin
* `pytest_hooklog`_ log invocations of extension hooks to a file.
* `pytest_runner`_ collect and run test items and create reports.
* `pytest_execnetcleanup`_ cleanup execnet gateways during test function runs.
* `pytest_pytester`_ funcargs and support code for testing py.test's own functionality.
.. _`pytest_xfail`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_xfail.py
.. _`pytest_figleaf`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_figleaf.py
.. _`pytest_monkeypatch`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_monkeypatch.py
.. _`pytest_iocapture`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_iocapture.py
.. _`pytest_recwarn`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_recwarn.py
.. _`pytest_unittest`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_unittest.py
.. _`pytest_doctest`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_doctest.py
.. _`pytest_restdoc`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_restdoc.py
.. _`pytest_oejskit`: http://bitbucket.org/pedronis/js-infrastructure/src/tip/pytest_jstests.py
.. _`pytest_pocoo`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_pocoo.py
.. _`pytest_resultlog`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_resultlog.py
.. _`pytest_terminal`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_terminal.py
.. _`pytest_pdb`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_pdb.py
.. _`pytest_keyword`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_keyword.py
.. _`pytest_hooklog`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_hooklog.py
.. _`pytest_runner`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_runner.py
.. _`pytest_execnetcleanup`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_execnetcleanup.py
.. _`pytest_pytester`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_pytester.py

View File

@ -11,6 +11,8 @@ quickstart_: for getting started immediately.
features_: a walk through basic features and usage.
`available plugins`_: list of py.test plugins
funcargs_: powerful parametrized test function setup
`distributed testing`_: distribute test runs to other machines and platforms.
@ -21,6 +23,7 @@ config_: ``conftest.py`` files and the config object
talks_: talk and tutorial slides
.. _`available plugins`: plugins.html
.. _talks: talks.html
.. _quickstart: quickstart.html
.. _features: features.html

106
makepluginlist.py Normal file
View File

@ -0,0 +1,106 @@
import py
import sys
WIDTH = 75
plugins = [
('Plugins related to Python test functions and programs',
'xfail figleaf monkeypatch iocapture recwarn',),
('Plugins for other testing styles and languages',
'unittest doctest restdoc osjskit'),
('Plugins for generic reporting and failure logging',
'pocoo resultlog terminal',),
('internal plugins / core functionality',
'pdb keyword hooklog runner execnetcleanup pytester',
)
]
externals = {
'osjskit': ('`pytest_oejskit`_ Testing Javascript in real browsers',
'''
jskit contains infrastructure and in particular a py.test plugin to enable running tests for JavaScript code inside browsers directly using py.test as the test driver. Running inside the browsers comes with some speed cost, on the other hand it means for example the code is tested against the real-word DOM implementations.
The approach also enables to write integration tests such that the JavaScript code is tested against server-side Python code mocked as necessary. Any server-side framework that can already be exposed through WSGI (or for which a subset of WSGI can be written to accommodate the jskit own needs) can play along.
jskit also contains code to help modularizing JavaScript code which can be used to describe and track dependencies dynamically during development and that can help resolving them statically when deploying/packaging.
jskit depends on simplejson. It also uses MochiKit - of which it ships a version within itself for convenience - for its own working though in does not imposes its usage on tested code.
jskit was initially developed by Open End AB and is released under the MIT license.
''', 'http://pypi.python.org/pypi/oejskit',
('pytest_oejskit',
'http://bitbucket.org/pedronis/js-infrastructure/src/tip/pytest_jstests.py',
))}
class ExternalDoc:
def __init__(self, name):
self.title, self.longdesc, self.url, sourcelink = externals[name]
self.sourcelink = sourcelink
class PluginDoc:
def __init__(self, plugin):
self.plugin = plugin
doc = plugin.__doc__.strip()
i = doc.find("\n")
if i == -1:
title = doc
longdesc = "XXX no long description available"
else:
title = doc[:i].strip()
longdesc = doc[i+1:].strip()
purename = plugin.__name__.split(".")[-1].strip()
self.title = "`%s`_ %s" %(purename, title)
self.longdesc = longdesc
self.sourcelink = (purename,
"http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/" +
purename + ".py")
def warn(msg):
print >>sys.stderr, "WARNING:", msg
def makedoc(name):
if name in externals:
return ExternalDoc(name)
config.pluginmanager.import_plugin(name)
plugin = config.pluginmanager.getplugin(name)
if plugin is None:
return None
return PluginDoc(plugin)
def header():
#print "=" * WIDTH
#print "list of available py.test plugins"
#print "=" * WIDTH
print
if __name__ == "__main__":
config = py.test.config
config.parse([])
config.pluginmanager.do_configure(config)
header()
links = []
for cat, specs in plugins:
pluginlist = specs.split()
print len(cat) * "="
print cat
print len(cat) * "="
for name in pluginlist:
doc = makedoc(name)
if doc is None:
warn("skipping", name)
continue
print "* " + str(doc.title)
#print len(doc.title) * "*"
#print doc.longdesc
links.append(doc.sourcelink)
print
print
print
for link in links:
warn(repr(link))
print ".. _`%s`: %s" % (link[0], link[1])

View File

@ -1,10 +1,11 @@
"""
py.test hooks / extension points
py.test plugin hooks
"""
# ------------------------------------------------------------------------------
# Command line and configuration hooks
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Command line and configuration
# -------------------------------------------------------------------------
def pytest_addoption(parser):
""" called before commandline parsing. """
@ -17,26 +18,12 @@ def pytest_configure(config):
def pytest_namespace(config):
""" return dict of name->object to become available at py.test.*"""
def pytest_unconfigure(config):
""" called before test process is exited. """
# ------------------------------------------------------------------------------
# test Session related hooks
# ------------------------------------------------------------------------------
def pytest_sessionstart(session):
""" before session.main() is called. """
def pytest_sessionfinish(session, exitstatus, excrepr=None):
""" whole test run finishes. """
def pytest_deselected(items):
""" repeatedly called for test items deselected by keyword. """
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# collection hooks
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
def pytest_collect_directory(path, parent):
""" return Collection node or None for the given path. """
@ -50,6 +37,9 @@ def pytest_collectstart(collector):
def pytest_collectreport(rep):
""" collector finished collecting. """
def pytest_deselected(items):
""" called for test items deselected by keyword. """
def pytest_make_collect_report(collector):
""" perform a collection and return a collection. """
pytest_make_collect_report.firstresult = True
@ -58,9 +48,9 @@ pytest_make_collect_report.firstresult = True
def pytest_itemstart(item, node=None):
""" test item gets collected. """
# ------------------------------------------------------------------------------
# Python test function related hooks
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Python test function related hooks
# -------------------------------------------------------------------------
def pytest_pycollect_makeitem(collector, name, obj):
""" return custom item/collector for a python object in a module, or None. """
@ -73,9 +63,14 @@ pytest_pyfunc_call.firstresult = True
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# generic runtest related hooks
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
def pytest_runtest_protocol(item):
""" implement fixture, run and report protocol. """
pytest_runtest_protocol.firstresult = True
def pytest_runtest_setup(item):
""" called before pytest_runtest_call(). """
@ -85,10 +80,6 @@ def pytest_runtest_call(item):
def pytest_runtest_teardown(item):
""" called after pytest_runtest_call(). """
def pytest_runtest_protocol(item):
""" run given test item and return test report. """
pytest_runtest_protocol.firstresult = True
def pytest_runtest_makereport(item, call):
""" make ItemTestReport for the given item and call outcome. """
pytest_runtest_makereport.firstresult = True
@ -96,9 +87,20 @@ pytest_runtest_makereport.firstresult = True
def pytest_runtest_logreport(rep):
""" process item test report. """
# ------------------------------------------------------------------------------
# generic reporting hooks (invoked from pytest_terminal.py)
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# test session related hooks
# -------------------------------------------------------------------------
def pytest_sessionstart(session):
""" before session.main() is called. """
def pytest_sessionfinish(session, exitstatus, excrepr=None):
""" whole test run finishes. """
# -------------------------------------------------------------------------
# generic reporting hooks (invoked from pytest_terminal)
# -------------------------------------------------------------------------
def pytest_report_teststatus(rep):
""" return shortletter and verbose word. """
pytest_report_teststatus.firstresult = True
@ -112,33 +114,17 @@ def pytest_report_iteminfo(item):
"""
pytest_report_iteminfo.firstresult = True
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# doctest hooks
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
def pytest_doctest_prepare_content(content):
""" return processed content for a given doctest"""
pytest_doctest_prepare_content.firstresult = True
# ------------------------------------------------------------------------------
# misc hooks
# ------------------------------------------------------------------------------
def pytest_plugin_registered(plugin):
""" a new py lib plugin got registered. """
def pytest_plugin_unregistered(plugin):
""" a py lib plugin got unregistered. """
def pytest_internalerror(excrepr):
""" called for internal errors. """
def pytest_trace(category, msg):
""" called for debug info. """
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
# distributed testing
# ------------------------------------------------------------------------------
# -------------------------------------------------------------------------
def pytest_testnodeready(node):
""" Test Node is ready to operate. """
@ -152,3 +138,19 @@ def pytest_rescheduleitems(items):
def pytest_looponfailinfo(failreports, rootdirs):
""" info for repeating failing tests. """
# -------------------------------------------------------------------------
# internal debugging hooks
# -------------------------------------------------------------------------
def pytest_plugin_registered(plugin):
""" a new py lib plugin got registered. """
def pytest_plugin_unregistered(plugin):
""" a py lib plugin got unregistered. """
def pytest_internalerror(excrepr):
""" called for internal errors. """
def pytest_trace(category, msg):
""" called for debug info. """

View File

@ -1,5 +1,9 @@
"""
write and report coverage data using the 'figleaf' module.
write and report coverage data with 'figleaf'.
This plugin generates test coverage data or HTML files
from running tests against a code base.
"""
import py

View File

@ -1,11 +1,10 @@
"""
helpers for asserting deprecation and other warnings.
recwarn: function argument where one can call recwarn.pop() to get
the last warning that would have been shown.
**recwarn**: function argument where one can call recwarn.pop() to get
the last warning that would have been shown.
py.test.deprecated_call(func, *args, **kwargs):
assert that a function call triggers a deprecation warning.
**py.test.deprecated_call(func, *args, **kwargs)**: assert that the given function call triggers a deprecation warning.
"""
import py

View File

@ -6,7 +6,7 @@ import py
def pytest_addoption(parser):
group = parser.addgroup("resultlog", "resultlog plugin options")
group.addoption('--resultlog', action="store", dest="resultlog", metavar="path",
group.addoption('--resultlog', action="store", dest="resultlog", metavar="path", default=None,
help="path for machine-readable result log.")
def pytest_configure(config):

View File

@ -1,7 +1,7 @@
"""
mark tests as expected-to-fail and report them separately.
example:
example::
@py.test.mark.xfail
def test_hello():