sphinxcontrib-issuetracker-0.8/0000755000076400007640000000000011625250051017246 5ustar lunarlunar00000000000000sphinxcontrib-issuetracker-0.8/tox.ini0000644000076400007640000000056411623251115020566 0ustar lunarlunar00000000000000[tox] envlist=py27,doc [testenv] deps= -r{toxinidir}/tests/requirements.txt commands= py.test {posargs:--junitxml={envname}-tests.xml} [testenv:doc] basepython=python2 deps= -r{toxinidir}/doc commands= sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html sphinxcontrib-issuetracker-0.8/doc/0000755000076400007640000000000011625250051020013 5ustar lunarlunar00000000000000sphinxcontrib-issuetracker-0.8/doc/license.rst0000644000076400007640000000006011623245331022166 0ustar lunarlunar00000000000000License ======= .. literalinclude:: ../LICENSE sphinxcontrib-issuetracker-0.8/doc/conf.py0000644000076400007640000000562111625113645021325 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import os import sys import re from sphinx import addnodes doc_directory = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.normpath( os.path.join(doc_directory, os.pardir, 'sphinxcontrib'))) import issuetracker needs_sphinx = '1.0' extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.autodoc', 'sphinxcontrib.issuetracker'] source_suffix = '.rst' master_doc = 'index' project = u'sphinxcontrib-issuetracker' copyright = u'2010, 2011, Sebastian Wiesner' version = '.'.join(issuetracker.__version__.split('.')[:2]) release = issuetracker.__version__ exclude_patterns = ['_build/*'] html_theme = 'default' html_static_path = [] intersphinx_mapping = {'python': ('http://docs.python.org/', None), 'sphinx': ('http://sphinx.pocoo.org/', None)} issuetracker = 'bitbucket' issuetracker_project = 'birkenfeld/sphinx-contrib' event_sig_re = re.compile(r'([a-zA-Z-]+)\s*\((.*)\)') def parse_event(env, sig, signode): m = event_sig_re.match(sig) if not m: signode += addnodes.desc_name(sig, sig) return sig name, args = m.groups() signode += addnodes.desc_name(name, name) plist = addnodes.desc_parameterlist() for arg in args.split(','): arg = arg.strip() plist += addnodes.desc_parameter(arg, arg) signode += plist return name def setup(app): app.add_description_unit('confval', 'confval', 'pair: %s; configuration value') app.add_description_unit('event', 'event', 'pair: %s; event', parse_event) sphinxcontrib-issuetracker-0.8/doc/changes.rst0000644000076400007640000000007311623245331022160 0ustar lunarlunar00000000000000######### Changelog ######### .. include:: ../CHANGES.rst sphinxcontrib-issuetracker-0.8/doc/Makefile0000644000076400007640000001107611623245331021463 0ustar lunarlunar00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sphinxcontrib-issuetracker.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sphinxcontrib-issuetracker.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/sphinxcontrib-issuetracker" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sphinxcontrib-issuetracker" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." sphinxcontrib-issuetracker-0.8/doc/index.rst0000644000076400007640000001637011625211076021667 0ustar lunarlunar00000000000000:mod:`sphinxcontrib.issuetracker` -- Reference issues in issue trackers ======================================================================= .. module:: sphinxcontrib.issuetracker :synopsis: Parse issue references and link to the corresponding issues A Sphinx_ extension to turn textual issue ids like ``#10`` into real references to these issues in an issue tracker. The extension is available under the terms of the BSD license, see :doc:`license` for more information. Installation ------------ This extension needs Sphinx 1.0 and Python 2.6 or newer. Python 3 is not (yet) supported. Use ``pip`` to install this extension straight from the Python Package Index:: pip install sphinx-contrib.issuetracker Operation --------- After configuring the :confval:`tracker ` and the :confval:`project `, this extension parses issue ids in all documents and turns the issue id into a reference to the issue. Issues are searched in the configured tracker, issue ids which do not exist are ignored. Issue ids in ``inline literals`` or literal blocks are also ignored to not spoil code examples. The extension queries the tracker for information about each issue. This information is used to mark closed issues. Such issues are automatically struck through in HTML output. Configuration ------------- Add ``sphinxcontrib.issuetracker`` to the configuration value :confval:`extensions` to enable this extensions and configure the extension: .. confval:: issuetracker The issuetracker to use. As of now, the following trackers are supported: - ``github``: The issue tracker of https://github.com. - ``bitbucket``: The issue tracker of https://bitbucket.org. - ``launchpad``: The issue tracker of https://launchpad.net. To use this issue tracker, launchpadlib_ must be installed. - ``google code``: The issue tracker of http://code.google.com. - ``debian``: The Debian issue tracker at http://bugs.debian.org. To use this issue tracker, debianbts_ and SOAPpy_ must be installed. - ``jira``: A Jira_ instance. With this issue tracker :confval:`issuetracker_url` must be set to the base url of the Jira instance to use. Otherwise a :exc:`~exceptions.ValueError` is raised when resolving the first issue reference. .. versionadded:: 0.8 .. confval:: issuetracker_project The project inside the issue tracker or the project, to which the issue tracker belongs. Defaults to the value of :confval:`project`. .. note:: In case of BitBucket and GitHub, the project name must include the name of the user or organization, the project belongs to. For instance, the project name of Sphinx_ itself is not just ``sphinx``, but ``birkenfeld/sphinx`` instead. If the user name is missing, a :exc:`~exceptions.ValueError` will be raised when an issue is to be resolved the first time. .. versionchanged:: 0.8 Project names must include the user name now. .. confval:: issuetracker_url The base url of the issue tracker:: issuetracker = 'jira' issuetracker_url = 'https://studio.atlassian.com' Required by all issue trackers which do not only have a single instance, but many different instances on many different sites. .. versionadded:: 0.8 For instance, with the following configuration issue references in the documentation would refer to the `Sphinx issue tracker`_:: issuetracker = 'bitbucket' issuetracker_project = 'birkenfeld/sphinx' By default the extension looks for issue references starting with a single dash, like ``#10``. You can however change the pattern, which is used to find issue references: .. confval:: issuetracker_issue_pattern A regular expression, which is used to find and parse issue references. Defaults to ``r'#(\d+)'``. If changed to ``r'gh-(\d+)'`` for instance, this extension would not longer recognize references like ``#10``, but instead parse references like ``gh-10``. The pattern must contain only a single group, which matches the issue id. Normally the issue id as found in the text will be used as reference text. However, you can also use the title of the issue as issue text: .. confval:: issuetracker_expandtitle If ``True``, use the issue title instead of the issue id as reference text. If an issue has no title, the issue id is used instead, just like if this configuration key was ``False``. Defaults to ``False``. .. versionadded:: 0.8 Customization ------------- If you use an issue tracker that is not supported by this extension, then set :confval:`issuetracker` to ``None`` or leave it unset, and connect your own callback to the event :event:`issuetracker-resolve-issue`: .. event:: issuetracker-resolve-issue(app, tracker_config, issue_id) Emitted if a issue reference is to be resolved. ``app`` is the Sphinx application object. ``tracker_config`` is the issuetracker configuration as :class:`TrackerConfig` object. ``issue_id`` is the issue id as string. A callback should return an :class:`Issue` object containing the resolved issue, or ``None`` if it could not resolve the issue. In the latter case other resolvers connected to the event may be invoked by Sphinx. .. versionchanged:: 0.8 Replaced ``project`` argument with ``tracker_config``, changed return value from dictionary to :class:`Issue` .. autoclass:: TrackerConfig .. attribute:: project The project name as string .. attribute:: url The url of the issue tracker as string *without* any trailing slash, or ``None``, if there is no url configured for this tracker. See :confval:`issuetracker_url`. .. versionadded:: 0.8 .. class:: Issue A :func:`~collections.namedtuple` providing issue information. .. attribute:: id The issue id as string If you are writing your own custom callback for :event:`issuetracker-resolve-issue`, set this attribute to the ``issue_id`` that was given as argument. .. attribute:: url An URL providing information about this issue. This URL is used as hyperlink target in the generated documentation. Thus it should point to a webpage or something similar that provides human-readable information about an issue. .. attribute:: closed ``True``, if the issue is closed, ``False`` otherwise. .. versionadded:: 0.8 Contribution and Development ---------------------------- Please report bugs and missing functionality (e.g. a tracker not being supported) to the `issue tracker`_. The source code of this extension is available in the sphinx-contrib_ repository. Feel free to clone this repository and add your changes to this extension. Patches and pull requests are always welcome! .. include:: ../CREDITS .. toctree:: :maxdepth: 2 :hidden: changes.rst license.rst .. _Sphinx: http://sphinx.pocoo.org/ .. _Sphinx issue tracker: https://bitbucket.org/birkenfeld/sphinx/issues/ .. _jira: http://www.atlassian.com/software/jira/ .. _launchpadlib: http://pypi.python.org/pypi/launchpadlib/ .. _debianbts: http://pypi.python.org/pypi/python-debianbts/ .. _SOAPpy: http://pypi.python.org/pypi/SOAPpy/ .. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib .. _issue tracker: https://bitbucket.org/birkenfeld/sphinx-contrib/issues/ sphinxcontrib-issuetracker-0.8/CHANGES.rst0000644000076400007640000000557511625247557021105 0ustar lunarlunar000000000000000.8 (2011 Aug, 24) ================== Incompatible changes -------------------- - Require Python 2.6 or newer now - Removed ``issuetracker_user`` configuration value, GitHub and BitBucket projects must include the username now - Custom resolvers must return :class:`~sphinxcontrib.issuetracker.Issue` objects instead of dictionaries now - Signature of :event:`issuetracker-resolve-issue` changed Other changes ------------- * General: - Builtin ``debian`` tracker is fully supported now * New features: - Added Jira_ support - Added :confval:`issuetracker_url` - Added :confval:`issuetracker_expandtitle` * Bugs fixes and improvements: - Use BitBucket API instead of scraping the BitBucket website - Cache failed issue lookups, too .. _jira: http://www.atlassian.com/software/jira/ 0.7.2 (Mar 10, 2011) ==================== - #13: Fixed source distribution to include tests again - Fixed extraction of issue state for open issues from bitbucket - Ignore references in inline literals and literal blocks 0.7.1 (Jan 19, 2011) ==================== - Fixed #8: Copy the stylesheet after build again to avoid exceptions on non-existing build directories 0.7 (Jan 08, 2011) ================== - Issue information is now cached - Custom issue trackers must now connect to the ``issuetracker-resolve-issue`` event, the builtin ``missing-reference`` event is no longer used. 0.6 (Jan 04, 2011) ================== - Added support for the debian bugtracker (thanks to Fladischer Michael) - Fixed NameError in launchpad issue tracker - Bitbucket also uses HTTPS urls now 0.5.4 (Nov 15, 2010) ==================== - Github uses HTTPS urls now 0.5.3 (Nov 14, 2010) ==================== - Added license text to source tarball 0.5.2 (Sep 17, 2010) ==================== - Issue reference resolvers get the application object now as fourth argument. The environment is availabe in the ``.env`` attribute of this object. - #4: Fixed the URL of Google Code issues (thanks to Denis Bilenko) - Fixed detection of closed issues in Google Code (thanks to Denis Bilenko) - Improved error message, if ``issuetracker_issue_pattern`` has too many groups (thanks to Denis Bilenko) - Added warnings for unexpected HTTP status codes in BitBucket and Google Code issue trackers 0.5.1 (Jul 25, 2010) ==================== - Fixed client string for launchpad access 0.5 (Jul 21, 2010) ================== - Closed issues are automatically struck trough in HTML output - Require Sphinx 1.0 now - Fixed #2: Installation on windows 0.4 (May 21, 2010) ================== - Misc spelling fixes 0.3 (May 02, 2010) ================== - Added support for Google Code - Added support for Launchpad - Issue tracker callbacks get the build environment now 0.2 (Apr 13, 2010) ================== - Use ``missing-reference`` event instead of custom event 0.1 (Apr 10, 2010) ================== - Initial release sphinxcontrib-issuetracker-0.8/README0000644000076400007640000000257711625247536020157 0ustar lunarlunar00000000000000########################## sphinxcontrib-issuetracker ########################## http://packages.python.org/sphinxcontrib-issuetracker A Sphinx_ extension to turn textual issue ids like ``#10`` into real references to these issues in an issue tracker. Currently the following issue trackers are supported: - `GitHub `_ - `BitBucket `_ - `Launchpad `_ - `Google Code `_ - `Debian BTS `_ - `Jira `_ A simple API is provided to add support for other issue trackers. If you added support for a new tracker, please consider sending a patch to make your work available to other users of this extension. Installation ------------ This extension can be installed from the Python Package Index:: pip install sphinx-contrib.issuetracker Alternatively, you can clone the sphinx-contrib_ repository from BitBucket, and install the extension directly from the repository:: hg clone http://bitbucket.org/birkenfeld/sphinx-contrib cd sphinx-contrib/issuetracker python setup.py install Usage ----- Please refer to the documentation_ for further information. .. _Sphinx: http://sphinx.pocoo.org/latest .. _sphinx-contrib: http://bitbucket.org/birkenfeld/sphinx-contrib .. _documentation: http://packages.python.org/sphinxcontrib-issuetracker sphinxcontrib-issuetracker-0.8/setup.py0000644000076400007640000000551611623245331020772 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import os import re from setuptools import setup, find_packages with open('README') as stream: long_desc = stream.read() VERSION_PATTERN = re.compile(r"__version__ = '([^']+)'") def read_version_number(): with open(os.path.join('sphinxcontrib', 'issuetracker.py')) as stream: for line in stream: match = VERSION_PATTERN.search(line) if match: return match.group(1) else: raise ValueError('Could not extract version number') setup( name='sphinxcontrib-issuetracker', version=read_version_number(), url='http://packages.python.org/sphinxcontrib-issuetracker', download_url='http://pypi.python.org/pypi/sphinxcontrib-issuetracker', license='BSD', author='Sebastian Wiesner', author_email='lunaryorn@googlemail.com', description='Sphinx integration with different issuetrackers', long_description=long_desc, zip_safe=False, classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Documentation', 'Topic :: Utilities', ], platforms='any', packages=find_packages(), include_package_data=True, install_requires=['Sphinx>=1.0.7'], namespace_packages=['sphinxcontrib'], ) sphinxcontrib-issuetracker-0.8/PKG-INFO0000644000076400007640000000473011625250051020347 0ustar lunarlunar00000000000000Metadata-Version: 1.0 Name: sphinxcontrib-issuetracker Version: 0.8 Summary: Sphinx integration with different issuetrackers Home-page: http://packages.python.org/sphinxcontrib-issuetracker Author: Sebastian Wiesner Author-email: lunaryorn@googlemail.com License: BSD Download-URL: http://pypi.python.org/pypi/sphinxcontrib-issuetracker Description: ########################## sphinxcontrib-issuetracker ########################## http://packages.python.org/sphinxcontrib-issuetracker A Sphinx_ extension to turn textual issue ids like ``#10`` into real references to these issues in an issue tracker. Currently the following issue trackers are supported: - `GitHub `_ - `BitBucket `_ - `Launchpad `_ - `Google Code `_ - `Debian BTS `_ - `Jira `_ A simple API is provided to add support for other issue trackers. If you added support for a new tracker, please consider sending a patch to make your work available to other users of this extension. Installation ------------ This extension can be installed from the Python Package Index:: pip install sphinx-contrib.issuetracker Alternatively, you can clone the sphinx-contrib_ repository from BitBucket, and install the extension directly from the repository:: hg clone http://bitbucket.org/birkenfeld/sphinx-contrib cd sphinx-contrib/issuetracker python setup.py install Usage ----- Please refer to the documentation_ for further information. .. _Sphinx: http://sphinx.pocoo.org/latest .. _sphinx-contrib: http://bitbucket.org/birkenfeld/sphinx-contrib .. _documentation: http://packages.python.org/sphinxcontrib-issuetracker Platform: any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Documentation Classifier: Topic :: Utilities sphinxcontrib-issuetracker-0.8/sphinxcontrib/0000755000076400007640000000000011625250051022140 5ustar lunarlunar00000000000000sphinxcontrib-issuetracker-0.8/sphinxcontrib/issuetracker.py0000644000076400007640000003716011625231370025230 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2010, 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ sphinxcontrib.issuetracker ========================== Integration with isse trackers. Replace textual issue ids (like #1) with a reference to the corresponding issue in an issue tracker. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) __version__ = '0.8' import re import urllib2 from contextlib import closing from collections import namedtuple from os import path from docutils import nodes from docutils.transforms import Transform from sphinx.addnodes import pending_xref from sphinx.util.osutil import copyfile from sphinx.util.console import bold Issue = namedtuple('Issue', 'id title url closed') _TrackerConfig = namedtuple('_TrackerConfig', 'project url') class TrackerConfig(_TrackerConfig): """ Issue tracker configuration. This class provides configuration for trackers, and is passed as ``tracker_config`` arguments to callbacks of :event:`issuetracker-resolve-issue`. """ def __new__(cls, project, url=None): if url: url = url.rstrip('/') return _TrackerConfig.__new__(cls, project, url) @classmethod def from_sphinx_config(cls, config): project = config.issuetracker_project or config.project url = config.issuetracker_url return cls(project, url) def fetch_issue(app, url, output_format=None, opener=None): """ Fetch issue data from a web service or website. ``app`` is the sphinx application object. ``url`` is the url of the issue. ``output_format`` is the format of the data retrieved from the given ``url``. If set, it must be either ``'json'`` or ``'xml'``. ``opener`` is a :class:`urllib2.OpenerDirectory` object used to open the url. If ``None``, the global opener is used. Return the raw data retrieved from url, if ``output_format`` is unset. If ``output_format`` is ``'xml'``, return a ElementTree document. If ``output_format`` is ``'json'``, return the object parsed from JSON (typically a dictionary). Return ``None``, if ``url`` returned a status code other than 200. """ if output_format not in ('json', 'xml'): raise ValueError(output_format) if opener: urlopen = opener.open else: urlopen = urllib2.urlopen try: with closing(urlopen(url)) as response: if output_format == 'json': import json return json.load(response) elif output_format == 'xml': from xml.etree import cElementTree as etree return etree.parse(response) else: return response except urllib2.HTTPError as error: if error.code != 404: # 404 just says that the issue doesn't exist, but anything else is # more serious and deserves a warning app.warn('{0} unavailable with code {1}'.format(url, error.code)) return None def check_project_with_username(tracker_config): if '/' not in tracker_config.project: raise ValueError( 'username missing in project name: {0.project}'.format( tracker_config)) GITHUB_API_URL = 'https://api.github.com/repos/{0.project}/issues/{1}' def lookup_github_issue(app, tracker_config, issue_id): check_project_with_username(tracker_config) issue = fetch_issue(app, GITHUB_API_URL.format(tracker_config, issue_id), output_format='json') if issue: closed = issue['state'] == 'closed' return Issue(id=issue_id, title=issue['title'], closed=closed, url=issue['html_url']) BITBUCKET_URL = 'https://bitbucket.org/{0.project}/issue/{1}/' BITBUCKET_API_URL = ('https://api.bitbucket.org/1.0/repositories/' '{0.project}/issues/{1}/') def lookup_bitbucket_issue(app, tracker_config, issue_id): check_project_with_username(tracker_config) issue = fetch_issue(app, BITBUCKET_API_URL.format(tracker_config, issue_id), output_format='json') if issue: closed = issue['status'] not in ('new', 'open') url = BITBUCKET_URL.format(tracker_config, issue_id) return Issue(id=issue_id, title=issue['title'], closed=closed, url=url) DEBIAN_URL = 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug={0}' def lookup_debian_issue(app, tracker_config, issue_id): import debianbts try: # get the bug bug = debianbts.get_status(issue_id)[0] except IndexError: return None # check if issue matches project if tracker_config.project not in (bug.package, bug.source): return None return Issue(id=issue_id, title=bug.subject, closed=bug.done, url=DEBIAN_URL.format(issue_id)) LAUNCHPAD_URL = 'https://bugs.launchpad.net/bugs/{0}' def lookup_launchpad_issue(app, tracker_config, issue_id): launchpad = getattr(app.env, 'issuetracker_launchpad', None) if not launchpad: from launchpadlib.launchpad import Launchpad launchpad = Launchpad.login_anonymously( 'sphinxcontrib.issuetracker', service_root='production') app.env.issuetracker_launchpad = launchpad try: # get the bug bug = launchpad.bugs[issue_id] except KeyError: return None for task in bug.bug_tasks: if task.bug_target_name == tracker_config.project: break else: # no matching task found return None return Issue(id=issue_id, title=task.title, closed=bool(task.date_closed), url=LAUNCHPAD_URL.format(issue_id)) GOOGLE_CODE_URL = 'http://code.google.com/p/{0.project}/issues/detail?id={1}' GOOGLE_CODE_API_URL = ('http://code.google.com/feeds/issues/p/' '{0.project}/issues/full/{1}') def lookup_google_code_issue(app, tracker_config, issue_id): issue = fetch_issue(app, GOOGLE_CODE_API_URL.format( tracker_config, issue_id), output_format='xml') if issue: ISSUE_NS = '{http://schemas.google.com/projecthosting/issues/2009}' ATOM_NS = '{http://www.w3.org/2005/Atom}' state = issue.find('{0}state'.format(ISSUE_NS)) title_node = issue.find('{0}title'.format(ATOM_NS)) title = title_node.text if title_node is not None else None closed = state is not None and state.text == 'closed' return Issue(id=issue_id, title=title, closed=closed, url=GOOGLE_CODE_URL.format(tracker_config, issue_id)) JIRA_API_URL = ('{0.url}/si/jira.issueviews:issue-xml/{1}/{1}.xml?' # only request the required fields 'field=link&field=resolution&field=summary&field=project') def lookup_jira_issue(app, tracker_config, issue_id): if not tracker_config.url: raise ValueError('URL required') # protected jira trackers may require cookie processing from cookielib import CookieJar cookies = CookieJar() cookie_opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies)) issue = fetch_issue(app, JIRA_API_URL.format(tracker_config, issue_id), output_format='xml', opener=cookie_opener) if issue: project = issue.find('*/item/project').text if project != tracker_config.project: return None url = issue.find('*/item/link').text state = issue.find('*/item/resolution').text # summary contains the title without the issue id title = issue.find('*/item/summary').text closed = state.lower() != 'unresolved' return Issue(id=issue_id, title=title, closed=closed, url=url) BUILTIN_ISSUE_TRACKERS = { 'github': lookup_github_issue, 'bitbucket': lookup_bitbucket_issue, 'debian': lookup_debian_issue, 'launchpad': lookup_launchpad_issue, 'google code': lookup_google_code_issue, 'jira': lookup_jira_issue, } class IssuesReferences(Transform): """ Parse and transform issue ids in a document. Issue ids are parsed in text nodes and transformed into :class:`~sphinx.addnodes.pending_xref` nodes for further processing in later stages of the build. """ default_priority = 999 def apply(self): config = self.document.settings.env.config tracker_config = TrackerConfig.from_sphinx_config(config) issue_pattern = config.issuetracker_issue_pattern if isinstance(issue_pattern, basestring): issue_pattern = re.compile(issue_pattern) for node in self.document.traverse(nodes.Text): parent = node.parent if isinstance(parent, (nodes.literal, nodes.FixedTextElement)): # ignore inline and block literal text continue text = unicode(node) new_nodes = [] last_issue_ref_end = 0 for match in issue_pattern.finditer(text): # catch invalid pattern with too many groups if len(match.groups()) != 1: raise ValueError( 'issuetracker_issue_pattern must have ' 'exactly one group: {0!r}'.format(match.groups())) # extract the text between the last issue reference and the # current issue reference and put it into a new text node head = text[last_issue_ref_end:match.start()] if head: new_nodes.append(nodes.Text(head)) # adjust the position of the last issue reference in the # text last_issue_ref_end = match.end() # extract the issue text (including the leading dash) issuetext = match.group(0) # extract the issue number (excluding the leading dash) issue_id = match.group(1) # turn the issue reference into a reference node refnode = pending_xref() refnode['reftarget'] = issue_id refnode['reftype'] = 'issue' refnode['trackerconfig'] = tracker_config refnode['expandtitle'] = config.issuetracker_expandtitle refnode.append(nodes.Text(issuetext)) new_nodes.append(refnode) if not new_nodes: # no issue references were found, move on to the next node continue # extract the remaining text after the last issue reference, and # put it into a text node tail = text[last_issue_ref_end:] if tail: new_nodes.append(nodes.Text(tail)) # find and remove the original node, and insert all new nodes # instead parent.replace(node, new_nodes) def make_issue_reference(issue, content_node): """ Create a reference node for the given issue. ``content_node`` is a docutils node which is supposed to be added as content of the created reference. ``issue`` is the :class:`Issue` which the reference shall point to. Return a :class:`docutils.nodes.reference` for the issue. """ reference = nodes.reference() reference['refuri'] = issue.url if issue.closed: reference['classes'].append('issue-closed') reference['classes'].append('reference-issue') reference.append(content_node) return reference def lookup_issue(app, tracker_config, issue_id): """ Lookup the given issue. The issue is first looked up in an internal cache. If it is not found, the event ``issuetracker-resolve-issue`` is emitted. The result of this invocation is then cached and returned. ``app`` is the sphinx application object. ``tracker_config`` is the :class:`TrackerConfig` object representing the issue tracker configuration. ``issue_id`` is a string containing the issue id. Return a :class:`Issue` object for the issue with the given ``issue_id``, or ``None`` if the issue wasn't found. """ cache = app.env.issuetracker_cache if issue_id not in cache: issue = app.emit_firstresult('issuetracker-resolve-issue', tracker_config, issue_id) cache[issue_id] = issue return cache[issue_id] def resolve_issue_references(app, doctree): """ Resolve all pending issue references in the given ``doctree``. """ for node in doctree.traverse(pending_xref): if node['reftype'] == 'issue': issue = lookup_issue(app, node['trackerconfig'], node['reftarget']) if not issue: new_node = node[0] else: if node['expandtitle'] and issue.title: content_node = nodes.Text(issue.title) else: content_node = node[0] new_node = make_issue_reference(issue, content_node) node.replace_self(new_node) def connect_builtin_tracker(app): if app.config.issuetracker: app.connect(b'issuetracker-resolve-issue', BUILTIN_ISSUE_TRACKERS[app.config.issuetracker.lower()]) def add_stylesheet(app): app.add_stylesheet('issuetracker.css') def init_cache(app): if not hasattr(app.env, 'issuetracker_cache'): app.env.issuetracker_cache = {} def copy_stylesheet(app, exception): if app.builder.name != 'html' or exception: return app.info(bold('Copying issuetracker stylesheet... '), nonl=True) dest = path.join(app.builder.outdir, '_static', 'issuetracker.css') source = path.join(path.abspath(path.dirname(__file__)), 'issuetracker.css') copyfile(source, dest) app.info('done') def setup(app): app.require_sphinx('1.0') app.add_transform(IssuesReferences) app.add_event(b'issuetracker-resolve-issue') app.connect(b'builder-inited', connect_builtin_tracker) app.add_config_value('issuetracker_issue_pattern', re.compile(r'#(\d+)'), 'env') app.add_config_value('issuetracker_project', None, 'env') app.add_config_value('issuetracker_url', None, 'env') app.add_config_value('issuetracker_expandtitle', False, 'env') app.add_config_value('issuetracker', None, 'env') app.connect(b'builder-inited', add_stylesheet) app.connect(b'builder-inited', init_cache) app.connect(b'doctree-read', resolve_issue_references) app.connect(b'build-finished', copy_stylesheet) sphinxcontrib-issuetracker-0.8/sphinxcontrib/__init__.py0000644000076400007640000000055611623245331024262 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- """ sphinxcontrib ~~~~~~~~~~~~~ This package is a namespace package that contains all extensions distributed in the ``sphinx-contrib`` distribution. :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ __import__('pkg_resources').declare_namespace(__name__) sphinxcontrib-issuetracker-0.8/sphinxcontrib/issuetracker.css0000644000076400007640000000027111623245331025361 0ustar lunarlunar00000000000000.issue-closed { text-decoration: line-through; } .issue-closed:hover { text-decoration: line-through underline; } .issue-closed:visited { text-decoration: line-through; } sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/0000755000076400007640000000000011625250051026416 5ustar lunarlunar00000000000000sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/PKG-INFO0000644000076400007640000000473011625250031027515 0ustar lunarlunar00000000000000Metadata-Version: 1.0 Name: sphinxcontrib-issuetracker Version: 0.8 Summary: Sphinx integration with different issuetrackers Home-page: http://packages.python.org/sphinxcontrib-issuetracker Author: Sebastian Wiesner Author-email: lunaryorn@googlemail.com License: BSD Download-URL: http://pypi.python.org/pypi/sphinxcontrib-issuetracker Description: ########################## sphinxcontrib-issuetracker ########################## http://packages.python.org/sphinxcontrib-issuetracker A Sphinx_ extension to turn textual issue ids like ``#10`` into real references to these issues in an issue tracker. Currently the following issue trackers are supported: - `GitHub `_ - `BitBucket `_ - `Launchpad `_ - `Google Code `_ - `Debian BTS `_ - `Jira `_ A simple API is provided to add support for other issue trackers. If you added support for a new tracker, please consider sending a patch to make your work available to other users of this extension. Installation ------------ This extension can be installed from the Python Package Index:: pip install sphinx-contrib.issuetracker Alternatively, you can clone the sphinx-contrib_ repository from BitBucket, and install the extension directly from the repository:: hg clone http://bitbucket.org/birkenfeld/sphinx-contrib cd sphinx-contrib/issuetracker python setup.py install Usage ----- Please refer to the documentation_ for further information. .. _Sphinx: http://sphinx.pocoo.org/latest .. _sphinx-contrib: http://bitbucket.org/birkenfeld/sphinx-contrib .. _documentation: http://packages.python.org/sphinxcontrib-issuetracker Platform: any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Documentation Classifier: Topic :: Utilities sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/dependency_links.txt0000644000076400007640000000000111625250031032462 0ustar lunarlunar00000000000000 sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/top_level.txt0000644000076400007640000000001611625250031031143 0ustar lunarlunar00000000000000sphinxcontrib sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/requires.txt0000644000076400007640000000001511625250031031010 0ustar lunarlunar00000000000000Sphinx>=1.0.7sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/SOURCES.txt0000644000076400007640000000134611625250031030304 0ustar lunarlunar00000000000000CHANGES.rst LICENSE MANIFEST.in README setup.cfg setup.py tox.ini doc/Makefile doc/changes.rst doc/conf.py doc/index.rst doc/license.rst sphinxcontrib/__init__.py sphinxcontrib/issuetracker.css sphinxcontrib/issuetracker.py sphinxcontrib_issuetracker.egg-info/PKG-INFO sphinxcontrib_issuetracker.egg-info/SOURCES.txt sphinxcontrib_issuetracker.egg-info/dependency_links.txt sphinxcontrib_issuetracker.egg-info/namespace_packages.txt sphinxcontrib_issuetracker.egg-info/not-zip-safe sphinxcontrib_issuetracker.egg-info/requires.txt sphinxcontrib_issuetracker.egg-info/top_level.txt tests/conf.py tests/conftest.py tests/test_builtin_resolvers.py tests/test_resolval.py tests/test_setup.py tests/test_tracker_config.py tests/test_transformer.pysphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/not-zip-safe0000644000076400007640000000000111623246557030662 0ustar lunarlunar00000000000000 sphinxcontrib-issuetracker-0.8/sphinxcontrib_issuetracker.egg-info/namespace_packages.txt0000644000076400007640000000001611625250031032744 0ustar lunarlunar00000000000000sphinxcontrib sphinxcontrib-issuetracker-0.8/MANIFEST.in0000644000076400007640000000027711623245331021015 0ustar lunarlunar00000000000000include README include LICENSE include CHANGES.* include tox.ini recursive-include sphinxcontrib *.css recursive-include doc *.rst *.py Makefile recursive-include tests *.py prune doc/_build sphinxcontrib-issuetracker-0.8/tests/0000755000076400007640000000000011625250051020410 5ustar lunarlunar00000000000000sphinxcontrib-issuetracker-0.8/tests/test_tracker_config.py0000664000076400007640000001042711625230510025005 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from sphinxcontrib.issuetracker import TrackerConfig def pytest_funcarg__content(request): """ Dummy content for this test module, overrides the global ``content`` funcarg. This test module doesn't need issue references, but just configured sphinx application to check creating tracker configs from sphinx config. """ return 'dummy content' def test_tracker_config_only_project(): """ Test TrackerConfig constructor with a project only. """ tracker_config = TrackerConfig('eggs') assert tracker_config.project == 'eggs' def test_tracker_config_project_and_url(): """ Test TrackerConfig constructor with a project and url. """ tracker_config = TrackerConfig('eggs', 'http://example.com') assert tracker_config.project == 'eggs' assert tracker_config.url == 'http://example.com' def test_tracker_config_trailing_slash(): """ Test that the constructor removes trailing slashes from the url. """ tracker_config = TrackerConfig('eggs', 'http://example.com//') assert tracker_config.url == 'http://example.com' def test_tracker_config_equality(): """ Test equality and inequality of TrackerConfig objects. """ c = TrackerConfig assert c('eggs') == c('eggs') assert c('eggs') != c('spam') assert c('eggs') != c('eggs', 'spam') assert c('eggs', 'spam') == c('eggs', 'spam') assert c('eggs', 'spam') != c('eggs', 'foo') assert c('eggs', 'spam') != c('spam', 'spam') @pytest.mark.confoverrides( project='eggs', issuetracker_url='http://example.com') def test_tracker_config_from_sphinx_config_implicit_project(app): """ Test that TrackerConfig uses the Sphinx project name, if the issuetracker project was not explicitly set. """ tracker_config = TrackerConfig.from_sphinx_config(app.config) assert tracker_config.project == 'eggs' assert tracker_config.url == 'http://example.com' @pytest.mark.confoverrides(project='eggs', issuetracker_project='spam', issuetracker_url='http://example.com') def test_tracker_config_from_sphinx_config_explicit_project(app): """ Test that TrackerConfig uses the issuetracker project, if it was explicitly set. """ tracker_config = TrackerConfig.from_sphinx_config(app.config) assert tracker_config.project == 'spam' assert tracker_config.url == 'http://example.com' @pytest.mark.confoverrides( project='eggs', issuetracker_url='http://example.com//') def test_tracker_config_from_sphinx_config_trailing_slash(app): """ Test that TrackerConfig strips trailing slashes when creating from sphinx config, too. """ tracker_config = TrackerConfig.from_sphinx_config(app.config) assert tracker_config.project == 'eggs' assert tracker_config.url == 'http://example.com' sphinxcontrib-issuetracker-0.8/tests/conf.py0000644000076400007640000000332311625210000021676 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ Test configuration. .. moduleauthor:: Sebastian Wiesner """ extensions = ['sphinxcontrib.issuetracker'] source_suffix = '.rst' master_doc = 'index' project = u'issuetracker-test' copyright = u'2011, foo' version = '1' release = '1' exclude_patterns = [] pygments_style = 'sphinx' html_theme = 'default' sphinxcontrib-issuetracker-0.8/tests/test_transformer.py0000644000076400007640000001032511625245624024376 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from sphinxcontrib.issuetracker import Issue def pytest_funcarg__issue(request): """ A dummy issue, just to trigger issue resolval so that transformations can be seen in the output. """ return Issue(id='10', title='Eggs', closed=False, url='eggs') def pytest_funcarg__app(request): """ Adds the ``mock_resolver`` marker to the current test before creating the ``app``. """ request.applymarker(pytest.mark.mock_resolver) return request.getfuncargvalue('app') @pytest.mark.with_content('#10') def test_transform_simple(doctree): """ Test a simple transform with just an issue id. """ assert doctree.is_('reference') @pytest.mark.with_content('before #10 after') def test_transform_leading_and_trailing_text(doctree, content): """ Test that transformation leaves leading and trailing text intact. """ assert doctree.is_('reference') assert doctree.text() == content @pytest.mark.with_content('*#10* **#10**') def test_transform_inline_markup(doctree): """ Test that issue ids inside inline markup like emphasis are transformed. """ assert len(doctree.find('reference')) == 2 assert doctree.text() == '#10 #10' assert doctree.find('reference').eq(0).parents('emphasis') assert doctree.find('reference').eq(1).parents('strong') @pytest.mark.with_content('``#10``') def test_transform_literal(doctree): """ Test that transformation leaves literals untouched. """ assert not doctree.is_('reference') literal = doctree.find('literal') assert literal assert literal.text() == '#10' @pytest.mark.with_content("""\ spam:: eggs #10""") def test_transform_literal_block(doctree): """ Test that transformation leaves literal blocks untouched. """ assert not doctree.is_('reference') literal_block = doctree.find('literal_block') assert literal_block assert literal_block.text('eggs\n #10') @pytest.mark.with_content("""\ .. code-block:: python eggs('#10')""") def test_transform_code_block(doctree, content): assert not doctree.is_('reference') literal_block = doctree.find('literal_block') assert literal_block assert literal_block.text("eggs('#10')") assert literal_block.attr.language == 'python' @pytest.mark.with_content('ab') @pytest.mark.confoverrides(issuetracker_issue_pattern=r'(a)(b)') def test_too_many_groups(app): """ Test that using an issue pattern with too many groups fails with an understandable error message. """ with pytest.raises(ValueError) as excinfo: app.build() error = excinfo.value assert str(error) == ('issuetracker_issue_pattern must have ' 'exactly one group: {0!r}'.format((u'a', u'b'))) sphinxcontrib-issuetracker-0.8/tests/test_setup.py0000644000076400007640000000621311625230620023163 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import re BUILTIN_TRACKER_NAME_PATTERN = re.compile('lookup_(.*)_issue') import pytest from sphinxcontrib import issuetracker def pytest_funcarg__content(request): """ Dummy content for this test module, overrides the global ``content`` funcarg. This test module doesn't need issue references, but just a loaded and ready-to-build sphinx application. Thus the content doesn't matter, but still a sphinx application needs some content to build. """ return 'dummy content' def test_builtin_issue_trackers(): """ Test that all builtin issue trackers are really declared in the BUILTIN_ISSUE_TRACKERS dict. """ trackers = dict(issuetracker.BUILTIN_ISSUE_TRACKERS) for attr in dir(issuetracker): match = BUILTIN_TRACKER_NAME_PATTERN.match(attr) if match: tracker_name = match.group(1).replace('_', ' ') assert tracker_name in trackers trackers.pop(tracker_name) assert not trackers def test_unknown_tracker(app): """ Test that setting ``issuetracker`` to an unknown tracker fails. """ app.config.issuetracker = 'spamtracker' with pytest.raises(KeyError): issuetracker.connect_builtin_tracker(app) def test_add_stylesheet(app): """ Test that the stylesheet is properly added. """ from sphinx.builders.html import StandaloneHTMLBuilder assert '_static/issuetracker.css' in StandaloneHTMLBuilder.css_files def test_transform_added(app): """ Test that the transformer is properly added. """ from sphinx.environment import SphinxStandaloneReader assert issuetracker.IssuesReferences in SphinxStandaloneReader.transforms sphinxcontrib-issuetracker-0.8/tests/conftest.py0000644000076400007640000002367111625245072022627 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import os import pytest import py.path from mock import Mock from pyquery import PyQuery from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment import SphinxStandaloneReader from sphinxcontrib.issuetracker import Issue, IssuesReferences TEST_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) def assert_issue_reference(doctree, issue, title=False): """ pytest helper which asserts that the given ``doctree`` contains a single reference, which references the given ``issue``. If ``title`` is ``True``, it is expected that the reference text is the issue title, otherwise (the default) it is expected that the reference text is the issue id. Return the reference node. Raise :exc:`~exceptions.AssertionError` if the ``doctree`` doesn't contain a reference to the given ``issue``. """ __tracebackhide__ = True reference = doctree.find('reference') assert len(reference) == 1 assert reference.attr.refuri == issue.url classes = reference.attr.classes.split(' ') is_closed = 'issue-closed' in classes assert 'reference-issue' in classes assert issue.closed == is_closed if title: assert reference.text() == issue.title else: assert reference.text() == '#{0}'.format(issue.id) return reference def get_index_doctree(app): """ Get the doctree of the index document processed by the given ``app`` as XML. ``app`` is the sphinx application from which to get the doctree. Return a :class:`~pyquery.PyQuery` object representing the doctree. """ return PyQuery(str(app.env.get_doctree('index')), parser='xml') def pytest_namespace(): """ Add the following functions to the pytest namespace: - :func:`get_index_doctree` - :func:`assert_issue_reference` """ return dict((f.__name__, f) for f in (get_index_doctree, assert_issue_reference)) def pytest_configure(config): """ Configure issue tracker tests. Adds ``confpy`` attribute to ``config`` which provides the path to the test ``conf.py`` file. """ config.confpy = py.path.local(TEST_DIRECTORY).join('conf.py') def pytest_funcarg__content(request): """ The content for the test document as string. By default, the content is taken from the argument of the ``with_content`` marker. If no such marker exists, the content is build from the id returned by the ``issue_id`` funcargby prepending a dash before the id. The issue id ``'10'`` will thus produce the content ``'#10'``. If the ``issue_id`` funcarg returns ``None``, a :exc:`~exceptions.ValueError` is raised eventually. Test modules may override this funcarg to add their own content. """ content_mark = request.keywords.get('with_content') if content_mark: return content_mark.args[0] else: issue_id = request.getfuncargvalue('issue_id') if issue_id: return '#{0}'.format(issue_id) raise ValueError('no content provided') def pytest_funcarg__doctree(request): """ The doctree of the parsed and processed ``content`` as :class:`~pyquery.PyQuery` object. .. note:: This funcarg automatically builds the application to create the doctree. This happens *before* test execution! If you need to build inside the test, build manually and use :func:`get_index_doctree()` to get the doctree afterwards. """ app = request.getfuncargvalue('app') app.build() return pytest.get_index_doctree(app) def pytest_funcarg__srcdir(request): """ The Sphinx source directory for the current test as path. This directory contains the standard test ``conf.py`` and a single document named ``index.rst``. The content of this document is the return value of the ``content`` funcarg. """ tmpdir = request.getfuncargvalue('tmpdir') srcdir = tmpdir.join('src') srcdir.ensure(dir=True) confpy = request.getfuncargvalue('pytestconfig').confpy confpy.copy(srcdir) content = request.getfuncargvalue('content') srcdir.join('index.rst').write(content) return srcdir def pytest_funcarg__outdir(request): """ The Sphinx output directory for the current test as path. """ tmpdir = request.getfuncargvalue('tmpdir') return tmpdir.join('html') def pytest_funcarg__doctreedir(request): """ The Sphinx doctree directory for the current test as path. """ tmpdir = request.getfuncargvalue('tmpdir') return tmpdir.join('doctrees') def reset_global_state(): """ Remove global state setup by Sphinx. Makes sure that we got a fresh test application for each test. """ SphinxStandaloneReader.transforms.remove(IssuesReferences) StandaloneHTMLBuilder.css_files.remove('_static/issuetracker.css') def pytest_funcarg__confoverrides(request): """ Configuration value overrides for the current test as dictionary. By default this funcarg takes the configuration overrides from the keyword arguments of the ``confoverrides`` marker. If the marker doesn't exist, an empty dictionary is returned. Test modules may override this funcarg to return custom ``confoverrides``. """ confoverrides_marker = request.keywords.get('confoverrides') return confoverrides_marker.kwargs if confoverrides_marker else {} def pytest_funcarg__app(request): """ A Sphinx application for testing. The app uses the source directory from the ``srcdir`` funcarg, and writes to the directories given by the ``outdir`` and ``doctreedir`` funcargs. Additional configuration values can be inserted into this application through the ``confoverrides`` funcarg. If the marker ``mock_resolver`` is attached to the current test, the resolver callback returned by the ``mock_resolver`` funcarg is automatically connected to the ``issuetracker-resolve-issue`` event in the the created application. If the marker ``build_app`` is attached to the current test, the app is build before returning it. Otherwise you need to build explicitly in order to get the output. """ srcdir = request.getfuncargvalue('srcdir') outdir = request.getfuncargvalue('outdir') doctreedir = request.getfuncargvalue('doctreedir') confoverrides = request.getfuncargvalue('confoverrides') app = Sphinx(str(srcdir), str(srcdir), str(outdir), str(doctreedir), 'html',confoverrides=confoverrides, status=None, warning=None, freshenv=True) request.addfinalizer(reset_global_state) if 'mock_resolver' in request.keywords: lookup_mock_issue = request.getfuncargvalue('mock_resolver') app.connect(b'issuetracker-resolve-issue', lookup_mock_issue) if 'build_app' in request.keywords: app.build() return app def pytest_funcarg__issue(request): """ An :class:`~sphinxcontrib.issuetracker.Issue` for the current test, or ``None``, if no issue is to be used. By default, this funcarg creates an issue from the arguments of the ``with_issue`` marker, or returns ``None``, if there is no such marker on the current test. Test modules may override this funcarg to provide their own issues for tests. """ issue_marker = request.keywords.get('with_issue') if issue_marker: return Issue(*issue_marker.args, **issue_marker.kwargs) return None def pytest_funcarg__issue_id(request): """ The issue id for the current test, or ``None``, if no issue id is to be used. The issue id is taken from the ``id`` attribute of the issue returned by the ``issue`` funcarg. If the ``issue`` funcarg returns ``None``, this funcarg also returns ``None``. """ issue = request.getfuncargvalue('issue') if issue: return issue.id else: return None def pytest_funcarg__mock_resolver(request): """ A mocked callback for the ``issuetracker-resolve-issue`` event as :class:`~mock.Mock` object. If the ``issue`` funcarg doesn't return ``None``, the callback will return this issue if the issue id given to the callback matches the id of this issue. Otherwise it will always return ``None``. """ lookup_mock_issue = Mock(name='lookup_mock_issue', return_value=None) issue = request.getfuncargvalue('issue') if issue: def lookup(app, tracker_config, issue_id): return issue if issue_id == issue.id else None lookup_mock_issue.side_effect = lookup return lookup_mock_issue sphinxcontrib-issuetracker-0.8/tests/test_builtin_resolvers.py0000644000076400007640000002731511625245162025612 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from sphinxcontrib.issuetracker import Issue, TrackerConfig try: import debianbts except ImportError: debianbts = None try: import launchpadlib except ImportError: launchpadlib = None def pytest_generate_tests(metafunc): """ Generate tests. Generate tests for all test functions with an ``issue`` argument by adding calls for each tests in testname in the ``issues`` attribute of the test class, the function is defined in. The ``issues`` attribute is expected to be a mapping from test names to Issue objects which this test is expected to resolve to, or with issue ids as string, if the test is expected to be unable to resolve the issue. """ if 'issue' in metafunc.funcargnames: for testname in sorted(metafunc.cls.issues): metafunc.addcall(id=testname, param=testname) def pytest_funcarg__testname(request): """ The testname as string, or ``None``, if no testname is known. This is the parameter added by the test generation hook, or ``None`` if no parameter was set, because test generation didn't add a call for this test. """ return getattr(request, 'param', None) def pytest_funcarg__tracker(request): """ The tracker name as string, or ``None``, if no tracker is known. The tracker name is taken from the ``name`` attribute of the class this test is defined in. If the test isn't defined in a class, ``None`` is returned. """ if not request.cls: return None return request.cls.name def pytest_funcarg__tracker_config(request): """ The tracker configuration as ``TrackerConfig`` object, or ``None``, if there is no tracker configuration. Tracker configuration is taken from the class this test is defined in. If there is a ``testname`` for this test, the tracker config is taken from the ``tracker_config`` map defined in the class, falling back to the ``default_tracker_config`` defined in the class. If there is no ``testname``, the ``default_tracker_config`` is used right away. If the test isn't defined in a class, ``None`` is returned. """ cls = request.cls if cls is None: return None testname = getattr(request, 'param', None) if testname is None: return cls.default_tracker_config else: return cls.tracker_config.get(testname, cls.default_tracker_config) def pytest_funcarg__confoverrides(request): """ Confoverrides for this test as dictionary. Provides confoverrides for this test that include the tracker name and tracker configuration (as returned by the ``tracker`` and ``tracker_config`` funcargs). The ``expandtitle`` setting is enabled. The global ``confoverrides`` are included, and overwrite any configuration key set in this funcarg. """ # configure tracker and enable title expansion to test the title retrieval # of builtin trackers, too tracker = request.getfuncargvalue('tracker') confoverrides = dict(issuetracker=tracker, issuetracker_expandtitle=True) tracker_config = request.getfuncargvalue('tracker_config') if tracker_config: # bring tracker configuration in confoverrides.update(issuetracker_project=tracker_config.project, issuetracker_url=tracker_config.url) # bring test-class specific overrides in if request.cls: confoverrides.update(request.cls.confoverrides) # add overrides from the test itself confoverrides.update(request.getfuncargvalue('confoverrides')) return confoverrides def pytest_funcarg__issue_id(request): """ The issue id of this test as string, or ``None``, if this test doesn't have a ``testname``. The issue id is taken from the issue defined in the ``issues`` attribute of the class this test is defined in. """ testname = request.getfuncargvalue('testname') if not testname: return None issue = request.cls.issues[testname] if isinstance(issue, basestring): return issue else: return issue.id def pytest_funcarg__issue(request): """ The issue object for this test, or ``None``, if the test is expected to be unable to resolve the issue. The issue id is taken from the issue defined in the ``issues`` attribute of the class this test is defined in. """ testname = request.getfuncargvalue('testname') issue = request.cls.issues[testname] if isinstance(issue, basestring): return None else: return issue class TrackerTest(object): """ Base class for tests for builtin issue trackers. This class defines a single test which tests issue lookup. """ #: the name of the issuetracker to use in this test name = None #: the default tracker configuration default_tracker_config = None #: test-specific tracker configuration tracker_config = {} #: issues to test. the key is a descriptive name for the issue #: (e.g. resolved, invalid or something the like of), the value is either a #: plain string, in which case the test is expected to not resolve the #: issue, or an ``Issue`` object, in which case the test is expected to #: resolve the issue id to excately this issue object issues = {} #: confoverrides to use for tests defined in this class confoverrides = {} def test_lookup(self, app, doctree, issue_id, issue): """ Test that this tracker correctly looks up an issue. """ assert app.env.issuetracker_cache == {issue_id: issue} if not issue: assert not doctree.is_('reference') else: pytest.assert_issue_reference(doctree, issue, title=True) class ScopedProjectTrackerTest(TrackerTest): """ Base class for tests for issue trackers which use scoped project names including the user name. Defines an additional tests which tests for exceptions raised if the username is missing. """ @pytest.mark.with_content('#10') @pytest.mark.confoverrides(issuetracker_project='eggs') def test_project_missing_username(self, app): """ Test that a project name without an username fails with a ValueError. """ with pytest.raises(ValueError) as excinfo: app.build() assert str(excinfo.value) == \ 'username missing in project name: eggs' class TestBitBucket(ScopedProjectTrackerTest): name = 'bitbucket' default_tracker_config = TrackerConfig('birkenfeld/sphinx') tracker_config = {'no project': TrackerConfig('lunar/foobar')} SPHINX_URL = 'https://bitbucket.org/birkenfeld/sphinx/issue/{0}/' issues = { 'resolved': Issue(id='478', closed=True, url=SPHINX_URL.format('478'), title='Adapt py:decorator from Python docs'), 'invalid': Issue(id='327', closed=True, url=SPHINX_URL.format('327'), title='Spaces at the end of console messages'), 'duplicate': Issue(id='733', closed=True, url=SPHINX_URL.format('733'), title='byte/str conversion fails on Python 3.2'), 'no project': '10', 'no issue': '10000' } class TestGitHub(ScopedProjectTrackerTest): name = 'github' default_tracker_config = TrackerConfig('lunaryorn/pyudev') tracker_config = {'no project': TrackerConfig('lunaryorn/foobar')} issues = { 'closed': Issue(id='2', title=u'python 3 support', closed=True, url='https://github.com/lunaryorn/pyudev/issues/2'), 'no project': '10', 'no issue': '1000', } class TestGoogleCode(TrackerTest): name = 'google code' default_tracker_config = TrackerConfig('pytox') tracker_config = {'no project': TrackerConfig('foobar')} PYTOX_URL = 'http://code.google.com/p/pytox/issues/detail?id={0}' issues = { 'fixed': Issue(id='2', closed=True, url=PYTOX_URL.format('2'), title='Hudson exists with SUCCESS status even if tox ' 'failed with ERROR'), 'invalid': Issue(id='5', title='0.7: "error: File exists"', closed=True, url=PYTOX_URL.format('5')), 'wontfix': Issue(id='6', title='Copy modules from site packages', closed=True, url=PYTOX_URL.format('6')), 'no issue': '1000', 'no project': '1', } class TestDebian(TrackerTest): pytestmark = pytest.mark.skipif(b'debianbts is None') name = 'debian' tracker_config = {'fixed': TrackerConfig('ldb-tools'), 'no project': TrackerConfig('release.debian.org')} DEBIAN_URL = 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug={0}' issues = { 'fixed': Issue(id='584227', title='ldb-tools: missing ldb(7) manpage', closed=True, url=DEBIAN_URL.format('584227')), 'no project': '1', } class TestLaunchpad(TrackerTest): pytestmark = pytest.mark.skipif(b'launchpadlib is None') name = 'launchpad' default_tracker_config = TrackerConfig('inkscape') issues = { 'closed': Issue('647789', title='tries to install file(s) outside of ' './configure\'s --prefix', closed=True, url='https://bugs.launchpad.net/bugs/647789') } class TestJira(TrackerTest): name = 'jira' issues = { 'resolved': Issue('SHERPA-15', closed=True, title='Breadcrumbs and ' 'page title missing from admin screens', url='https://studio.atlassian.com/browse/SHERPA-15'), 'open': Issue('PYO-84', closed=False, title='Implement LLSD login in pyogp', url='https://jira.secondlife.com/browse/PYO-84'), } tracker_config = { 'resolved': TrackerConfig('Sherpa', 'https://studio.atlassian.com'), 'open': TrackerConfig('Pyogp', 'https://jira.secondlife.com'), } confoverrides = dict(issuetracker_issue_pattern=r'#([A-Z]+-\d+)') @pytest.mark.with_content('#FOO-15') def test_no_url(self, app): """ Test that the jira tracker fails with a ValueError, if no URL was configured. """ with pytest.raises(ValueError) as excinfo: app.build() assert str(excinfo.value) == 'URL required' sphinxcontrib-issuetracker-0.8/tests/test_resolval.py0000644000076400007640000000730111625245061023656 0ustar lunarlunar00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from sphinxcontrib.issuetracker import TrackerConfig, Issue def pytest_funcarg__app(request): """ Adds the ``mock_resolver`` marker to the current test before creating the ``app``. """ request.applymarker(pytest.mark.mock_resolver) return request.getfuncargvalue('app') @pytest.mark.with_content('#10') def test_no_issue(app, doctree): """ Test that no reference is created if an issue could not be resolved. """ assert app.env.issuetracker_cache == {'10': None} assert not doctree.is_('reference') @pytest.mark.with_issue(id='10', title='Spam', url='spam', closed=False) def test_open_issue(app, doctree, issue): """ Test resolval of an open issue. """ assert app.env.issuetracker_cache == {'10': issue} pytest.assert_issue_reference(doctree, issue) @pytest.mark.with_issue(id='10', title='Eggs', url='eggs', closed=True) def test_closed_issue(app, doctree, issue): """ Test resolval of a closed issue. """ assert app.env.issuetracker_cache == {'10': issue} pytest.assert_issue_reference(doctree, issue) @pytest.mark.confoverrides(issuetracker_expandtitle=True) @pytest.mark.with_issue(id='10', title='Eggs', url='eggs', closed=True) def test_closed_issue_with_title(app, doctree, issue): """ Test resolval of an issue with title expansion enabled. """ assert app.env.issuetracker_cache == {'10': issue} pytest.assert_issue_reference(doctree, issue, title=True) @pytest.mark.build_app @pytest.mark.with_content('#10') def test_event_emitted(app, mock_resolver): """ Test that issue resolval emits the event with the right arguments. """ assert mock_resolver.call_count == 1 mock_resolver.assert_called_with( app, TrackerConfig.from_sphinx_config(app.config), '10') @pytest.mark.build_app @pytest.mark.with_content('#10 #10 #11 #11') @pytest.mark.with_issue(id='10', title='Eggs', closed=True, url='eggs') def test_event_emitted_only_once(app, mock_resolver, issue): """ Test that the resolval event is only emitted once for each issue id, and that subsequent lookups hit the cache. """ assert mock_resolver.call_count == 2 assert app.env.issuetracker_cache == {'10': issue, '11': None} sphinxcontrib-issuetracker-0.8/setup.cfg0000644000076400007640000000021711625250051021067 0ustar lunarlunar00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [aliases] release = egg_info -RDb '' [upload_docs] upload_dir = build/sphinx/html sphinxcontrib-issuetracker-0.8/LICENSE0000644000076400007640000000245611623245331020265 0ustar lunarlunar00000000000000Copyright (c) 2010, Sebastian Wiesner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.