sphinxcontrib-issuetracker-0.11/0000755000076500000240000000000012075757304020023 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/CHANGES.rst0000644000076500000240000001026012075757220021621 0ustar swiesnerstaff000000000000000.11 (Jan 17, 2013) =================== - Send proper user agent in API requests - #4: Respect Github rate limits - #5: Fix compatibility with requests 1.0 0.10.1 (Jun 19, 2012) ===================== - Fix README rendering on PyPI - #1: Fix test failure on Python 3 0.10 (Jun 18, 2012) =================== - Extension now hosted at https://github.com/lunaryorn/sphinxcontrib-issuetracker - Use requests library for HTTP requests - Consider launchpad issues closed only if all referenced tasks are completed - Support Python 3 (with exception of ``launchpad`` and ``debianbts`` trackers) 0.9 (Aug 31, 2011) ================== Incompatible changes -------------------- - Remove :confval:`issuetracker_expandtitle`, use ``issuetracker_title_template = '{issue.title}'`` instead - Rename :event:`issuetracker-resolve-issue` to :event:`issuetracker-lookup-issue` Other changes ------------- * New features: - Add :rst:role:`issue` role for explicit issue references - Add :confval:`issuetracker_title_template` - Add :confval:`issuetracker_plaintext_issues` - Use issue title as link title * Bug fixes and improvements: - Fix TypeError caused by ``launchpad`` issue tracker - Fix issue title in ``launchpad`` issue tracker - Fix detection of closed issues in ``launchpad`` issue tracker - Fix CSS classes for issue references to be more compatible with Sphinx themes 0.8 (Aug 24, 2011) ================== Incompatible changes -------------------- - Require Python 2.6 or newer now - Remove ``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 - Change signature of :event:`issuetracker-resolve-issue` Other changes ------------- * General: - Builtin ``debian`` tracker is fully supported now * New features: - Add Jira_ support - Add :confval:`issuetracker_url` - Add :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) ==================== - Fix source distribution to include tests again - Fix extraction of issue state for open issues from bitbucket - Ignore references in inline literals and literal blocks 0.7.1 (Jan 19, 2011) ==================== - 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) ================== - Add support for the debian bugtracker (thanks to Fladischer Michael) - Fix NameError in launchpad issue tracker - Use HTTPS for BitBucket 0.5.4 (Nov 15, 2010) ==================== - Use HTTPS for Github 0.5.3 (Nov 14, 2010) ==================== - Add 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. - Fix the URL of Google Code issues (thanks to Denis Bilenko) - Fix detection of closed issues in Google Code (thanks to Denis Bilenko) - Improve error message, if ``issuetracker_issue_pattern`` has too many groups (thanks to Denis Bilenko) - Add warnings for unexpected HTTP status codes in BitBucket and Google Code issue trackers 0.5.1 (Jul 25, 2010) ==================== - Fix client string for launchpad access 0.5 (Jul 21, 2010) ================== - Closed issues are automatically struck trough in HTML output - Require Sphinx 1.0 now - Fix installation on Windows 0.4 (May 21, 2010) ================== - Misc spelling fixes 0.3 (May 02, 2010) ================== - Add support for Google Code - Add 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.11/CREDITS0000644000076500000240000000100111773072133021025 0ustar swiesnerstaff00000000000000Credits ------- sphinxcontrib-issuetracker is written and maintained by Sebastian Wiesner The following people, listed alphabetically, contributed to this extension or supported its development otherwise: * Ali-Akber Saifee – Expand title feature, Jira support * Denis Bilenko – Fixes to Google Code integration, reporting of issues * Michael Fladischer – Integration of DebianBTS, Debian packaging, testing, reporting of issues, general fixes Many thanks for all contributions! sphinxcontrib-issuetracker-0.11/doc/0000755000076500000240000000000012075757304020570 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/doc/changes.rst0000644000076500000240000000007311773072133022724 0ustar swiesnerstaff00000000000000######### Changelog ######### .. include:: ../CHANGES.rst sphinxcontrib-issuetracker-0.11/doc/conf.py0000644000076500000240000000537411773072133022072 0ustar swiesnerstaff00000000000000# -*- 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 re from sphinx import addnodes from 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, 2012 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 = 'github' issuetracker_project = 'lunaryorn/sphinxcontrib-issuetracker' 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.11/doc/configuration.rst0000644000076500000240000001105011773072133024160 0ustar swiesnerstaff00000000000000Configuration ============= General 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. This tracker is not supported on Python 3, because launchpadlib_ is not yet available for Python 3. - ``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. This issue tracker is not available on Python 3, because neither debianbts_ nor SOAPpy_ are available for Python 3 yet. - ``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 Plaintext issues ---------------- .. confval:: issuetracker_plaintext_issues If ``True`` (the default) issue references are extracted from plain text by turning issue ids like ``#10`` into references to the corresponding issue. Issue ids in any kind of literal text (e.g. ``inline literals`` or code blocks) are ignored. If ``False``, no issue references are created from plain text. Independently of this configuration value, you can always reference issues explicitly with the :rst:role:`issue` role. .. versionadded:: 0.9 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 reference title will be the whole issue id. However you can also use a custom reference title: .. confval:: issuetracker_title_template A `format string`_ template for the title of references created from plaintext issue ids. The format string gets the :class:`Issue` object corresponding to the referenced issue in the ``issue`` key, you may use any attributes of this object in your format string. You can for instance include the issue title and the issue id:: issuetracker_title_template = '{issue.title} ({issue.id})' If unset, the whole text matched by :confval:`issuetracker_issue_pattern` is used as reference title. .. versionadded:: 0.9 Replaces :confval:`issuetracker_expandtitle` .. _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://github.com/lunaryorn/sphinxcontrib-issuetracker .. _format string: http://docs.python.org/library/string.html#format-string-syntax sphinxcontrib-issuetracker-0.11/doc/customization.rst0000644000076500000240000000500611773072133024225 0ustar swiesnerstaff00000000000000Customization ============= .. module:: sphinxcontrib.issuetracker :synopsis: Parse issue references and link to the corresponding issues To use an issue tracker not supported by this extension, set :confval:`issuetracker` to ``None`` or leave it unset, and connect your own callback to the event :event:`issuetracker-lookup-issue`: .. event:: issuetracker-lookup-issue(app, tracker_config, issue_id) Emitted if the issue with the given ``issue_id`` should be looked up in the issue tracker. Issue tracker configured is provided by ``tracker_config``. ``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 looked up issue, or ``None`` if it could not find the issue. In the latter case other callbacks connected to this event are be invoked by Sphinx. .. versionchanged:: 0.8 Replaced ``project`` argument with ``tracker_config``, changed return value from dictionary to :class:`Issue` .. versionchanged:: 0.9 Renamed from :event:`issuetracker-resolve-issue` to :event:`issuetracker-lookup-issue` Refer to the `builtin trackers`_ for examples. Supporting classes ------------------ .. 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-lookup-issue`, set this attribute to the ``issue_id`` that was given as argument. .. attribute:: title The human readable title of this issue as string. .. attribute:: url A string containing an URL for 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 .. _builtin trackers: https://github.com/lunaryorn/sphinxcontrib-issuetracker/blob/master/sphinxcontrib/issuetracker/resolvers.py sphinxcontrib-issuetracker-0.11/doc/index.rst0000644000076500000240000000653411777056723022446 0ustar swiesnerstaff00000000000000sphinxcontrib.issuetracker -- Reference issues in issue trackers ================================================================ A Sphinx_ extension to reference issues in an issue tracker. The extension is available under the terms of the BSD license, see :doc:`license` for more information. Installation ------------ Use ``pip`` to install this extension straight from the `Python Package Index`_:: pip install sphinxcontrib-issuetracker This extension requires Sphinx 1.1 and Python 2.6 or Python 3.1. .. note:: Some builtin issue trackers do *not* support Python 3 currently. Refer to :confval:`issuetracker` for more information. Usage ----- Add this extension to :confval:`extensions` and configure the issue tracker to use and the project in your :file:`conf.py`:: extensions = ['sphinxcontrib.issuetracker'] issuetracker = 'github' issuetracker_project = 'lunaryorn/sphinxcontrib-issuetracker' Now issue references like ``#10`` are replaced with links to the `issue tracker`_ of this extension, unless the reference occurs in literal text like inline literals or code blocks. You can disable this magic behaviour by setting :confval:`issuetracker_plaintext_issues` to ``False``:: issuetracker_plaintext_issues = False Now textual references are no longer replaced. However, you can still explicitly reference issues with the :rst:role:`issue` role. For more details refer to the :doc:`usage` and :doc:`configuration` chapters. If this extension does not yet support the issue tracker you're using, read the :doc:`customization` chapter and implement support for your tracker. Support ------- Please report issues to the `issue tracker`_ if you have trouble, found a bug in this extension or lack support for a specific issue tracker, but respect the following rules: - Check that the issue has not already been reported. - Check that the issue is not already fixed in the ``master`` branch. - Open issues with clear title and a detailed description in grammatically correct, complete sentences. Development ----------- The source code is hosted on Github_: git clone https://github.com/lunaryorn/sphinxcontrib-issuetracker Please fork the repository and send pull requests with your fixes or cool new features, but respect the following rules: - Read `how to properly contribute to open source projects on GitHub `_. - Use a topic branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - Squash commits on the topic branch before opening a pull request. - Respect :pep:`8` (use `pep8`_ to check your coding style compliance) - Add unit tests - Open a `pull request `_ that relates to but one subject with a clear title and description in grammatically correct, complete sentences. .. include:: ../CREDITS Contents -------- .. toctree:: usage configuration customization changes license .. _Github: https://github.com/lunaryorn/sphinxcontrib-issuetracker .. _Sphinx: http://sphinx.pocoo.org/ .. _issue tracker: https://github.com/lunaryorn/sphinxcontrib-issuetracker/issues/ .. _pep8: http://pypi.python.org/pypi/pep8/ .. _python package index: http://pypi.python.org/pypi/sphinxcontrib-issuetracker sphinxcontrib-issuetracker-0.11/doc/license.rst0000644000076500000240000000006011773072133022732 0ustar swiesnerstaff00000000000000License ======= .. literalinclude:: ../LICENSE sphinxcontrib-issuetracker-0.11/doc/Makefile0000644000076500000240000001107611773072133022227 0ustar swiesnerstaff00000000000000# 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.11/doc/usage.rst0000644000076500000240000000417711773072133022431 0ustar swiesnerstaff00000000000000Usage and operation =================== After configuring the :confval:`tracker ` and the :confval:`project ` (and possibly the :confval:`tracker url `), you can reference issues in the issue tracker with the :rst:role:`issue` role: .. rst:role:: issue Create a reference to the given issue. This role understands the standard :ref:`cross-referencing syntax ` used by Sphinx. An explicit title given to this role is interpreted as `format string`_, which is formatted with the :class:`Issue` object representing the referenced issue available by the key ``issue``. You may use any attribute of the :class:`Issue` object in your format string. Use this feature to include information about the referenced issue in the reference title. For instance, you might use ``:issue:`{issue.title} (#{issue.id}) <10>``` to use the title and the id of the issue ``10`` as reference title. .. versionadded:: 0.9 Information about the issue (like the title) is retrieved from the configured issue tracker. Aside of providing it for reference titles, the extension also uses this information to mark closed issues in HTML output by striking the reference text through. For this purpose, a stylesheet is added to the generated HTML. You can provide your own styles for issue references by adding them to the ``.xref.issue`` and ``.xref.issue.closed`` selectors (the latter are closed issues). For instance, the following stylesheet uses red color for open, and green color for closed issues:: .xref.issue { color: green; } .xref.issue.closed { color: red; } Issue ids in plain text ----------------------- If :confval:`issuetracker_plaintext_issues` is ``True``, this extension also searches for issue ids like ``#10`` in plain text and turns them into issue references. Issue ids in literal text (e.g. inline literals or code blocks) are ignored. The pattern used to extract issue ids from plain text can be configured using :confval:`issuetracker_issue_pattern`. .. _format string: http://docs.python.org/library/string.html#format-string-syntax sphinxcontrib-issuetracker-0.11/LICENSE0000644000076500000240000000246411773072133021030 0ustar swiesnerstaff00000000000000Copyright (c) 2010, 2011, 2012 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. sphinxcontrib-issuetracker-0.11/MANIFEST.in0000644000076500000240000000032511773072133021553 0ustar swiesnerstaff00000000000000include README.rst include LICENSE include CREDITS include CHANGES.rst include tox.ini recursive-include sphinxcontrib *.css recursive-include doc *.rst *.py Makefile recursive-include tests *.py prune doc/_build sphinxcontrib-issuetracker-0.11/PKG-INFO0000644000076500000240000001203312075757304021117 0ustar swiesnerstaff00000000000000Metadata-Version: 1.0 Name: sphinxcontrib-issuetracker Version: 0.11 Summary: Sphinx integration with different issuetrackers Home-page: http://sphinxcontrib-issuetracker.readthedocs.org/ Author: Sebastian Wiesner Author-email: lunaryorn@gmail.com License: BSD Description: ########################## sphinxcontrib-issuetracker ########################## .. image:: https://secure.travis-ci.org/lunaryorn/sphinxcontrib-issuetracker.png :target: http://travis-ci.org/lunaryorn/sphinxcontrib-issuetracker http://sphinxcontrib-issuetracker.readthedocs.org/ A Sphinx_ extension to reference issues in issue trackers, either explicitly with an "issue" role or optionally implicitly by issue ids like ``#10`` in plaintext. 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 sphinxcontrib-issuetracker This extension requires Sphinx 1.1 and Pythonn 2.6 or Python 3.1. Usage ----- Just add this extension to ``extensions`` and configure your issue tracker:: extensions = ['sphinxcontrib.issuetracker'] issuetracker = 'github' issuetracker_project = 'lunaryorn/sphinxcontrib-issuetracker' Now issue references like ``#10`` are replaced with links to the issue tracker of this extension, unless the reference occurs in literal text like inline literals or code blocks. You can disable this magic behaviour by setting issuetracker_plaintext_issues to ``False``:: issuetracker_plaintext_issues = False Now textual references are no longer replaced. However, you can still explicitly reference issues with the ``issue`` role. For more details refer to the documentation_. Support ------- Please report issues to the `issue tracker`_ if you have trouble, found a bug in this extension or lack support for a specific issue tracker, but respect the following rules: - Check that the issue has not already been reported. - Check that the issue is not already fixed in the ``master`` branch. - Open issues with clear title and a detailed description in grammatically correct, complete sentences. Development ----------- The source code is hosted on Github_: git clone https://github.com/lunaryorn/sphinxcontrib-issuetracker Please fork the repository and send pull requests with your fixes or cool new features, but respect the following rules: - Read `how to properly contribute to open source projects on GitHub `_. - Use a topic branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - Squash commits on the topic branch before opening a pull request. - Respect :pep:`8` (use `pep8`_ to check your coding style compliance) - Add unit tests - Open a `pull request `_ that relates to but one subject with a clear title and description in grammatically correct, complete sentences. .. _Sphinx: http://sphinx.pocoo.org/latest .. _documentation: http://sphinxcontrib-issuetracker.readthedocs.org .. _Python package index: http://pypi.python.org/pypi/sphinxcontrib-issuetracker .. _issue tracker: https://github.com/lunaryorn/sphinxcontrib-issuetracker/issues/ .. _pep8: http://pypi.python.org/pypi/pep8/ Platform: any Classifier: Development Status :: 5 - Production/Stable 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Documentation Classifier: Topic :: Utilities sphinxcontrib-issuetracker-0.11/README.rst0000644000076500000240000000674111777056723021527 0ustar swiesnerstaff00000000000000########################## sphinxcontrib-issuetracker ########################## .. image:: https://secure.travis-ci.org/lunaryorn/sphinxcontrib-issuetracker.png :target: http://travis-ci.org/lunaryorn/sphinxcontrib-issuetracker http://sphinxcontrib-issuetracker.readthedocs.org/ A Sphinx_ extension to reference issues in issue trackers, either explicitly with an "issue" role or optionally implicitly by issue ids like ``#10`` in plaintext. 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 sphinxcontrib-issuetracker This extension requires Sphinx 1.1 and Pythonn 2.6 or Python 3.1. Usage ----- Just add this extension to ``extensions`` and configure your issue tracker:: extensions = ['sphinxcontrib.issuetracker'] issuetracker = 'github' issuetracker_project = 'lunaryorn/sphinxcontrib-issuetracker' Now issue references like ``#10`` are replaced with links to the issue tracker of this extension, unless the reference occurs in literal text like inline literals or code blocks. You can disable this magic behaviour by setting issuetracker_plaintext_issues to ``False``:: issuetracker_plaintext_issues = False Now textual references are no longer replaced. However, you can still explicitly reference issues with the ``issue`` role. For more details refer to the documentation_. Support ------- Please report issues to the `issue tracker`_ if you have trouble, found a bug in this extension or lack support for a specific issue tracker, but respect the following rules: - Check that the issue has not already been reported. - Check that the issue is not already fixed in the ``master`` branch. - Open issues with clear title and a detailed description in grammatically correct, complete sentences. Development ----------- The source code is hosted on Github_: git clone https://github.com/lunaryorn/sphinxcontrib-issuetracker Please fork the repository and send pull requests with your fixes or cool new features, but respect the following rules: - Read `how to properly contribute to open source projects on GitHub `_. - Use a topic branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - Squash commits on the topic branch before opening a pull request. - Respect :pep:`8` (use `pep8`_ to check your coding style compliance) - Add unit tests - Open a `pull request `_ that relates to but one subject with a clear title and description in grammatically correct, complete sentences. .. _Sphinx: http://sphinx.pocoo.org/latest .. _documentation: http://sphinxcontrib-issuetracker.readthedocs.org .. _Python package index: http://pypi.python.org/pypi/sphinxcontrib-issuetracker .. _issue tracker: https://github.com/lunaryorn/sphinxcontrib-issuetracker/issues/ .. _pep8: http://pypi.python.org/pypi/pep8/ sphinxcontrib-issuetracker-0.11/setup.cfg0000644000076500000240000000021712075757304021644 0ustar swiesnerstaff00000000000000[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.11/setup.py0000644000076500000240000000565012075752122021534 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, 2012, 2013, 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.rst') as stream: long_desc = stream.read() VERSION_PATTERN = re.compile(r"__version__ = '([^']+)'") VERSION_FILE = os.path.join('sphinxcontrib', 'issuetracker', '__init__.py') def read_version_number(): with open(VERSION_FILE) 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://sphinxcontrib-issuetracker.readthedocs.org/', license='BSD', author='Sebastian Wiesner', author_email='lunaryorn@gmail.com', description='Sphinx integration with different issuetrackers', long_description=long_desc, zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Documentation', 'Topic :: Utilities', ], platforms='any', packages=find_packages(), include_package_data=True, install_requires=['Sphinx>=1.1', 'requests>=1.1'], namespace_packages=['sphinxcontrib'], ) sphinxcontrib-issuetracker-0.11/sphinxcontrib/0000755000076500000240000000000012075757304022715 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/sphinxcontrib/__init__.py0000644000076500000240000000025011773072133025015 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- """ sphinxcontrib ~~~~~~~~~~~~~ Contains 3rd party Sphinx extensions. """ __import__('pkg_resources').declare_namespace(__name__) sphinxcontrib-issuetracker-0.11/sphinxcontrib/issuetracker/0000755000076500000240000000000012075757304025421 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/sphinxcontrib/issuetracker/__init__.py0000644000076500000240000003022312066436053027525 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2010, 2011, 2012 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 issue trackers. Provide explicit and (optionally) implicit (e.g. ``#10``) references to issues in issue trackers. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) __version__ = '0.11' import sys import re from os import path from collections import namedtuple from docutils import nodes from docutils.transforms import Transform from sphinx.roles import XRefRole from sphinx.addnodes import pending_xref from sphinx.util.osutil import copyfile from sphinx.util.console import bold # Python 2/3 compatibility aliases if sys.version_info[0] >= 3: string_type = str text_type = str else: string_type = basestring text_type = unicode 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-lookup-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): """ Get tracker configuration from ``config``. """ project = config.issuetracker_project or config.project url = config.issuetracker_url return cls(project, url) class IssueRole(XRefRole): """ Standard Sphinx cross-referencing role to reference issues. Supports standard Sphinx :ref:`cross-referencing syntax `. An explicit title is interpreted as :xref:`format string `, with the :class:`Issue` object of the referenced issue available by the ``issue`` key. """ innernodeclass = nodes.inline def process_link(self, env, refnode, has_explicit_title, title, target): # store the tracker config in the reference node refnode['trackerconfig'] = TrackerConfig.from_sphinx_config(env.config) return title, target class IssueReferences(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 title_template = config.issuetracker_title_template if isinstance(issue_pattern, string_type): 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 = text_type(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 reftitle = title_template or issuetext refnode.append(nodes.inline( issuetext, reftitle, classes=['xref', 'issue'])) 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.title: reference['reftitle'] = issue.title if issue.closed: content_node['classes'].append('closed') 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-lookup-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-lookup-issue', tracker_config, issue_id) cache[issue_id] = issue return cache[issue_id] def lookup_issues(app, doctree): """ Lookup issues found in the given ``doctree``. Each issue reference in the given ``doctree`` is looked up. Each lookup result is cached by mapping the referenced issue id to the looked up :class:`Issue` object (an existing issue) or ``None`` (a missing issue). The cache is available at ``app.env.issuetracker_cache`` and is pickled along with the environment. """ for node in doctree.traverse(pending_xref): if node['reftype'] == 'issue': lookup_issue(app, node['trackerconfig'], node['reftarget']) def resolve_issue_reference(app, env, node, contnode): """ Resolve an issue reference and turn it into a real reference to the corresponding issue. ``app`` and ``env`` are the Sphinx application and environment respectively. ``node`` is a ``pending_xref`` node representing the missing reference. It is expected to have the following attributes: - ``reftype``: The reference type - ``trackerconfig``: The :class:`TrackerConfig`` to use for this node - ``reftarget``: The issue id - ``classes``: The node classes References with a ``reftype`` other than ``'issue'`` are skipped by returning ``None``. Otherwise the new node is returned. If the referenced issue was found, a real reference to this issue is returned. The text of this reference is formatted with the :class:`Issue` object available in the ``issue`` key. The reference title is set to the issue title. If the issue is closed, the class ``closed`` is added to the new content node. Otherwise, if the issue was not found, the content node is returned. """ if node['reftype'] != 'issue': return None issue = lookup_issue(app, node['trackerconfig'], node['reftarget']) if not issue: return contnode else: classes = contnode['classes'] conttext = text_type(contnode[0]) formatted_conttext = nodes.Text(conttext.format(issue=issue)) formatted_contnode = nodes.inline(conttext, formatted_conttext, classes=classes) return make_issue_reference(issue, formatted_contnode) def connect_builtin_tracker(app): from sphinxcontrib.issuetracker.resolvers import BUILTIN_ISSUE_TRACKERS if app.config.issuetracker: tracker = BUILTIN_ISSUE_TRACKERS[app.config.issuetracker.lower()] app.connect(str('issuetracker-lookup-issue'), tracker) 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 init_transformer(app): if app.config.issuetracker_plaintext_issues: app.add_transform(IssueReferences) 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_role('issue', IssueRole()) app.add_event(str('issuetracker-lookup-issue')) app.connect(str('builder-inited'), connect_builtin_tracker) # general configuration app.add_config_value('issuetracker', None, 'env') app.add_config_value('issuetracker_project', None, 'env') app.add_config_value('issuetracker_url', None, 'env') # configuration specific to plaintext issue references app.add_config_value('issuetracker_plaintext_issues', True, 'env') app.add_config_value('issuetracker_issue_pattern', re.compile(r'#(\d+)'), 'env') app.add_config_value('issuetracker_title_template', None, 'env') app.connect(str('builder-inited'), add_stylesheet) app.connect(str('builder-inited'), init_cache) app.connect(str('builder-inited'), init_transformer) app.connect(str('doctree-read'), lookup_issues) app.connect(str('missing-reference'), resolve_issue_reference) app.connect(str('build-finished'), copy_stylesheet) sphinxcontrib-issuetracker-0.11/sphinxcontrib/issuetracker/issuetracker.css0000644000076500000240000000031011773072133030623 0ustar swiesnerstaff00000000000000.xref.issue.closed { text-decoration: line-through; } .xref.issue.closed:hover { text-decoration: line-through underline; } .xref.issue.closed:visited { text-decoration: line-through; } sphinxcontrib-issuetracker-0.11/sphinxcontrib/issuetracker/resolvers.py0000644000076500000240000001753212075757012030023 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2010, 2011, 2012, 2013 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.resolvers ==================================== Builtin resolvers for :mod:`sphinxcontrib.issuetracker`. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import time import requests from xml.etree import ElementTree as etree from sphinxcontrib.issuetracker import Issue, __version__ GITHUB_API_URL = 'https://api.github.com/repos/{0.project}/issues/{1}' BITBUCKET_URL = 'https://bitbucket.org/{0.project}/issue/{1}/' BITBUCKET_API_URL = ('https://api.bitbucket.org/1.0/repositories/' '{0.project}/issues/{1}/') DEBIAN_URL = 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug={0}' LAUNCHPAD_URL = 'https://bugs.launchpad.net/bugs/{0}' 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}') # namespaces required to parse XML returned by Google Code GOOGLE_ISSUE_NS = '{http://schemas.google.com/projecthosting/issues/2009}' ATOM_NS = '{http://www.w3.org/2005/Atom}' 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 check_project_with_username(tracker_config): if '/' not in tracker_config.project: raise ValueError( 'username missing in project name: {0.project}'.format( tracker_config)) HEADERS = { 'User-Agent': 'sphinxcontrib-issuetracker v{0}'.format(__version__) } def get(app, url): """ Get a response from the given ``url``. ``url`` is a string containing the URL to request via GET. ``app`` is the Sphinx application object. Return the :class:`~requests.Response` object on status code 200, or ``None`` otherwise. If the status code is not 200 or 404, a warning is emitted via ``app``. """ response = requests.get(url, headers=HEADERS) if response.status_code == requests.codes.ok: return response elif response.status_code != requests.codes.not_found: msg = 'GET {0.url} failed with code {0.status_code}' app.warn(msg.format(response)) def lookup_github_issue(app, tracker_config, issue_id): check_project_with_username(tracker_config) # Get rate limit information from the environment timestamp, limit_hit = getattr(app.env, 'github_rate_limit', (0, False)) if limit_hit and time.time() - timestamp > 3600: # Github limits applications hourly limit_hit = False if not limit_hit: url = GITHUB_API_URL.format(tracker_config, issue_id) response = get(app, url) if response: rate_remaining = response.headers.get('X-RateLimit-Remaining') if rate_remaining.isdigit() and int(rate_remaining) == 0: app.warn('Github rate limit hit') app.env.github_rate_limit = (time.time(), True) issue = response.json() closed = issue['state'] == 'closed' return Issue(id=issue_id, title=issue['title'], closed=closed, url=issue['html_url']) else: app.warn('Github rate limit exceeded, not resolving issue {0}'.format( issue_id)) return None def lookup_bitbucket_issue(app, tracker_config, issue_id): check_project_with_username(tracker_config) url = BITBUCKET_API_URL.format(tracker_config, issue_id) response = get(app, url) if response: issue = response.json() 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) 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)) def lookup_launchpad_issue(app, tracker_config, issue_id): from launchpadlib.launchpad import Launchpad launchpad = Launchpad.login_anonymously('sphinxcontrib.issuetracker') try: # get the bug bug = launchpad.bugs[issue_id] except KeyError: return None project_tasks = [task for task in bug.bug_tasks if task.bug_target_name == tracker_config.project] if not project_tasks: # no matching task found return None is_complete = all(t.is_complete for t in project_tasks) return Issue(id=issue_id, title=bug.title, closed=is_complete, url=LAUNCHPAD_URL.format(issue_id)) def lookup_google_code_issue(app, tracker_config, issue_id): url = GOOGLE_CODE_API_URL.format(tracker_config, issue_id) response = get(app, url) if response: issue = etree.fromstring(response.content) state = issue.find('{0}state'.format(GOOGLE_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)) def lookup_jira_issue(app, tracker_config, issue_id): if not tracker_config.url: raise ValueError('URL required') url = JIRA_API_URL.format(tracker_config, issue_id) response = get(app, url) if response: issue = etree.fromstring(response.content) 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, } sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/0000755000076500000240000000000012075757304027173 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/dependency_links.txt0000644000076500000240000000000112075757273033246 0ustar swiesnerstaff00000000000000 sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/namespace_packages.txt0000644000076500000240000000001612075757273033530 0ustar swiesnerstaff00000000000000sphinxcontrib sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/not-zip-safe0000644000076500000240000000000111770120716031411 0ustar swiesnerstaff00000000000000 sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/PKG-INFO0000644000076500000240000001203312075757273030274 0ustar swiesnerstaff00000000000000Metadata-Version: 1.0 Name: sphinxcontrib-issuetracker Version: 0.11 Summary: Sphinx integration with different issuetrackers Home-page: http://sphinxcontrib-issuetracker.readthedocs.org/ Author: Sebastian Wiesner Author-email: lunaryorn@gmail.com License: BSD Description: ########################## sphinxcontrib-issuetracker ########################## .. image:: https://secure.travis-ci.org/lunaryorn/sphinxcontrib-issuetracker.png :target: http://travis-ci.org/lunaryorn/sphinxcontrib-issuetracker http://sphinxcontrib-issuetracker.readthedocs.org/ A Sphinx_ extension to reference issues in issue trackers, either explicitly with an "issue" role or optionally implicitly by issue ids like ``#10`` in plaintext. 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 sphinxcontrib-issuetracker This extension requires Sphinx 1.1 and Pythonn 2.6 or Python 3.1. Usage ----- Just add this extension to ``extensions`` and configure your issue tracker:: extensions = ['sphinxcontrib.issuetracker'] issuetracker = 'github' issuetracker_project = 'lunaryorn/sphinxcontrib-issuetracker' Now issue references like ``#10`` are replaced with links to the issue tracker of this extension, unless the reference occurs in literal text like inline literals or code blocks. You can disable this magic behaviour by setting issuetracker_plaintext_issues to ``False``:: issuetracker_plaintext_issues = False Now textual references are no longer replaced. However, you can still explicitly reference issues with the ``issue`` role. For more details refer to the documentation_. Support ------- Please report issues to the `issue tracker`_ if you have trouble, found a bug in this extension or lack support for a specific issue tracker, but respect the following rules: - Check that the issue has not already been reported. - Check that the issue is not already fixed in the ``master`` branch. - Open issues with clear title and a detailed description in grammatically correct, complete sentences. Development ----------- The source code is hosted on Github_: git clone https://github.com/lunaryorn/sphinxcontrib-issuetracker Please fork the repository and send pull requests with your fixes or cool new features, but respect the following rules: - Read `how to properly contribute to open source projects on GitHub `_. - Use a topic branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - Squash commits on the topic branch before opening a pull request. - Respect :pep:`8` (use `pep8`_ to check your coding style compliance) - Add unit tests - Open a `pull request `_ that relates to but one subject with a clear title and description in grammatically correct, complete sentences. .. _Sphinx: http://sphinx.pocoo.org/latest .. _documentation: http://sphinxcontrib-issuetracker.readthedocs.org .. _Python package index: http://pypi.python.org/pypi/sphinxcontrib-issuetracker .. _issue tracker: https://github.com/lunaryorn/sphinxcontrib-issuetracker/issues/ .. _pep8: http://pypi.python.org/pypi/pep8/ Platform: any Classifier: Development Status :: 5 - Production/Stable 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Documentation Classifier: Topic :: Utilities sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/requires.txt0000644000076500000240000000003112075757273031572 0ustar swiesnerstaff00000000000000Sphinx>=1.1 requests>=1.1sphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/SOURCES.txt0000644000076500000240000000167512075757274031076 0ustar swiesnerstaff00000000000000CHANGES.rst CREDITS LICENSE MANIFEST.in README.rst setup.cfg setup.py tox.ini doc/Makefile doc/changes.rst doc/conf.py doc/configuration.rst doc/customization.rst doc/index.rst doc/license.rst doc/usage.rst sphinxcontrib/__init__.py sphinxcontrib/issuetracker/__init__.py sphinxcontrib/issuetracker/issuetracker.css sphinxcontrib/issuetracker/resolvers.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/conftest.py tests/pypi.py tests/test_builtin_trackers.py tests/test_lookup.py tests/test_pypi.py tests/test_resolval.py tests/test_role.py tests/test_setup.py tests/test_stylesheet.py tests/test_tracker_config.py tests/test_transformer.pysphinxcontrib-issuetracker-0.11/sphinxcontrib_issuetracker.egg-info/top_level.txt0000644000076500000240000000001612075757273031727 0ustar swiesnerstaff00000000000000sphinxcontrib sphinxcontrib-issuetracker-0.11/tests/0000755000076500000240000000000012075757304021165 5ustar swiesnerstaff00000000000000sphinxcontrib-issuetracker-0.11/tests/conftest.py0000644000076500000240000002770011773072133023364 0ustar swiesnerstaff00000000000000# -*- 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 mock import Mock from docutils import nodes from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment import SphinxStandaloneReader from sphinx.addnodes import pending_xref from sphinxcontrib.issuetracker import Issue, IssueReferences #: test configuration CONF_PY = """\ 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' """ def assert_issue_pending_xref(doctree, issue_id, title): """ pytest helper which assert that the given ``doctree`` contains a single *pending* reference, which reference the given ``issue_id`` and has the given ``title``. """ __tracebackhide__ = True assert len(doctree.traverse(pending_xref)) == 1 xref = doctree.next_node(pending_xref) assert xref assert xref['reftarget'] == issue_id assert xref.astext() == title content = xref.next_node(nodes.inline) assert content classes = set(content['classes']) assert classes == set(['xref', 'issue']) def assert_issue_xref(doctree, issue, title): """ pytest helper which asserts that the given ``doctree`` contains a single *resolved* reference, which references the given ``issue`` and has the given ``title``. Return the reference node. Raise :exc:`~exceptions.AssertionError` if the ``doctree`` doesn't contain a reference to the given ``issue``. """ __tracebackhide__ = True assert len(doctree.traverse(nodes.reference)) == 1 reference = doctree.next_node(nodes.reference) assert reference assert reference['refuri'] == issue.url assert reference.get('reftitle') == issue.title assert reference.astext() == title content = reference.next_node(nodes.inline) assert content classes = set(content['classes']) expected_classes = set(['xref', 'issue']) if issue.closed: expected_classes.add('closed') assert classes == expected_classes return reference def pytest_namespace(): """ Add the following functions to the pytest namespace: - :func:`get_index_doctree` - :func:`assert_issue_xref` """ return dict((f.__name__, f) for f in (assert_issue_xref, assert_issue_pending_xref)) def pytest_addoption(parser): """ Add --offline and --fast options to test runner. """ parser.addoption('--offline', action='store_true', help='Skip tests which require network connection') parser.addoption('--fast', action='store_true', help='Skip slow tests, implies --offline') def pytest_configure(config): """ Configure issue tracker tests. Evaluates ``--fast`` and ``--offline``, and adds ``confpy`` attribute to ``config`` which provides the path to the test ``conf.py`` file. """ config.run_fast = config.getvalue('fast') config.run_offline = config.run_fast or config.getvalue('offline') def pytest_runtest_setup(item): """ Evaluate ``needs_network`` and ``slow`` markers with respect to ``--offline`` and ``--fast`` """ if item.config.run_offline and 'needs_network' in item.keywords: pytest.skip('network test in offline mode') if item.config.run_fast and 'slow' in item.keywords: pytest.skip('skipping slow test in fast mode') 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__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) srcdir.join('conf.py').write(CONF_PY.encode('utf-8'), 'wb') content = request.getfuncargvalue('content') srcdir.join('index.rst').write(content.encode('utf-8'), 'wb') 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 pytest_funcarg__doctree(request): """ The transformed doctree of the ``content``. .. note:: This funcarg builds the application before test execution. """ app = request.getfuncargvalue('app') app.build() return app.env.get_doctree('index') def pytest_funcarg__resolved_doctree(request): """ The resolved doctree of the ``content``. .. note:: This funcarg builds the application before test execution. """ app = request.getfuncargvalue('app') app.build() return app.env.get_and_resolve_doctree('index', app.builder) def pytest_funcarg__cache(request): """ Return the issue tracker cache. .. note:: This funcarg builds the application before test execution. """ app = request.getfuncargvalue('app') app.build() return app.env.issuetracker_cache def pytest_funcarg__index_html_file(request): """ Return the path of the ``index.html`` created by building. This file contains the ``content`` rendered as HTML. The ``app`` is build by this funcarg to generate the ``index.html`` file. """ app = request.getfuncargvalue('app') app.build() outdir = request.getfuncargvalue('outdir') return outdir.join('index.html') def reset_global_state(): """ Remove global state setup by Sphinx. Makes sure that we got a fresh test application for each test. """ try: SphinxStandaloneReader.transforms.remove(IssueReferences) except ValueError: pass 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_lookup`` is attached to the current test, the lookup callback returned by the ``mock_lookup`` funcarg is automatically connected to the ``issuetracker-lookup-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_lookup' in request.keywords: lookup_mock_issue = request.getfuncargvalue('mock_lookup') app.connect(str('issuetracker-lookup-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_lookup(request): """ A mocked callback for the ``issuetracker-lookup-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.11/tests/pypi.py0000644000076500000240000001254412075754621022525 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2009 Python Software Foundation # Copyright (c) 2013 Sebastian Wiesner # # 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. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. """ pypi ~~~~ This file contains code to render ReST description like PyPI does, in order to test whether our README is correct. This code is taken and adapted from https://bitbucket.org/loewis/pypi/src/tip/description_utils.py, hence the different license of this file. """ from __future__ import (print_function, unicode_literals, absolute_import, division) import sys from urlparse import urlparse # Work around docutils #3596884, see # http://sourceforge.net/tracker/?func=detail&aid=3596884&group_id=38414&atid=422030 import docutils.utils from docutils import io, readers from docutils.core import publish_doctree, Publisher from docutils.transforms import TransformError def trim_docstring(text): """ Trim indentation and blank lines from docstring text & return it. See PEP 257. """ if not text: return text # Convert tabs to spaces (following the normal Python rules) # and split into a list of lines: lines = text.expandtabs().splitlines() # Determine minimum indentation (first line doesn't count): indent = sys.maxint for line in lines[1:]: stripped = line.lstrip() if stripped: indent = min(indent, len(line) - len(stripped)) # Remove indentation (first line is special): trimmed = [lines[0].strip()] if indent < sys.maxint: for line in lines[1:]: trimmed.append(line[indent:].rstrip()) # Strip off trailing and leading blank lines: while trimmed and not trimmed[-1]: trimmed.pop() while trimmed and not trimmed[0]: trimmed.pop(0) # Return a single string: return '\n'.join(trimmed) ALLOWED_SCHEMES = '''file ftp gopher hdl http https imap mailto mms news nntp prospero rsync rtsp rtspu sftp shttp sip sips snews svn svn+ssh telnet wais'''.split() def process_description(source, output_encoding='unicode'): """Given an source string, returns an HTML fragment as a string. The return value is the contents of the tag. Parameters: - `source`: A multi-line text string; required. - `output_encoding`: The desired encoding of the output. If a Unicode string is desired, use the default value of "unicode" . """ # Dedent all lines of `source`. source = trim_docstring(source) settings_overrides = { 'raw_enabled': 0, # no raw HTML code 'file_insertion_enabled': 0, # no file/URL access 'halt_level': 2, # at warnings or errors, raise an exception 'report_level': 5, # never report problems with the reST code } parts = None # Convert reStructuredText to HTML using Docutils. document = publish_doctree(source=source, settings_overrides=settings_overrides) for node in document.traverse(): if node.tagname == '#text': continue if node.hasattr('refuri'): uri = node['refuri'] elif node.hasattr('uri'): uri = node['uri'] else: continue o = urlparse(uri) if o.scheme not in ALLOWED_SCHEMES: raise TransformError('link scheme not allowed: {0}'.format(uri)) # now turn the transformed document into HTML reader = readers.doctree.Reader(parser_name='null') pub = Publisher(reader, source=io.DocTreeInput(document), destination_class=io.StringOutput) pub.set_writer('html') pub.process_programmatic_settings(None, settings_overrides, None) pub.set_destination(None, None) pub.publish() parts = pub.writer.parts output = parts['body'] if output_encoding != 'unicode': output = output.encode(output_encoding) return output def main(): filename = sys.argv[1] with open(filename) as source: output = process_description(source.read().decode('utf-8')) print(output) if __name__ == '__main__': main() sphinxcontrib-issuetracker-0.11/tests/test_builtin_trackers.py0000644000076500000240000003017012075753774026152 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, 2012, 2013, 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_builtin_resolvers ====================== Test builtin issue resolvers. These tests are mainly intended to make sure that changes to the extension internals or to the public API of the issue trackers don't break resolval. .. moduleauthor:: Sebastian Wiesner """ 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 ``title_template`` setting is set to ``{issue.title}``. 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_title_template='{issue.title}') 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] return issue.id if isinstance(issue, Issue) else issue 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] return issue if isinstance(issue, Issue) else None 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 exactly this issue object issues = {} #: confoverrides to use for tests defined in this class confoverrides = {} @pytest.mark.needs_network def test_lookup(self, cache, issue_id, issue): """ Test that this tracker correctly looks up an issue. """ assert cache == {issue_id: issue} 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='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(str('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(str('launchpadlib is None')) name = 'launchpad' default_tracker_config = TrackerConfig('inkscape') tracker_config = {'wrong project': TrackerConfig('foo'), 'invalid': TrackerConfig('eject (Ubuntu)')} issues = { 'closed': Issue('647789', title='tries to install file(s) outside of ' './configure\'s --prefix', closed=True, url='https://bugs.launchpad.net/bugs/647789'), 'invalid': Issue('173307', closed=True, title='All users should be able to eject CDs and ' 'removable media', url='https://bugs.launchpad.net/bugs/173307'), 'wrong project': '1000', 'no issue': '1000000', } 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'), } 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.11/tests/test_lookup.py0000644000076500000240000000724611773072133024112 0ustar swiesnerstaff00000000000000# -*- 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_lookup =========== Test issue lookup. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import pickle import pytest from sphinxcontrib.issuetracker import TrackerConfig def pytest_funcarg__app(request): """ Adds the ``mock_lookup`` and ``build_app`` markers to the current test before creating the ``app``. """ request.applymarker(pytest.mark.mock_lookup) request.applymarker(pytest.mark.build_app) return request.getfuncargvalue('app') @pytest.mark.with_content('#10') @pytest.mark.with_issue(id='10', title='Eggs', closed=False, url='eggs') def test_lookup_existing_issue(cache, issue): """ Test resolval of an existing issue. """ assert cache == {'10': issue} @pytest.mark.with_content('#10') def test_lookup_missing_issue(cache): """ Test resolval of a missing issue. """ assert cache == {'10': None} @pytest.mark.build_app @pytest.mark.with_content('#10 #11') @pytest.mark.with_issue(id='10', title='Eggs', closed=True, url='eggs') def test_cache_pickled(cache, doctreedir, issue): environment_file = doctreedir.join('environment.pickle') with environment_file.open('rb') as source: env = pickle.load(source) # check that the pickled cache matches the real cache assert env.issuetracker_cache == cache # and check that it actually contains what it is supposed to contain assert env.issuetracker_cache == {'10': issue, '11': None} @pytest.mark.build_app @pytest.mark.with_content('#10') def test_event_emitted(app, mock_lookup): """ Test that issue resolval emits the event with the right arguments. """ assert mock_lookup.call_count == 1 mock_lookup.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_lookup, issue): """ Test that the resolval event is only emitted once for each issue id, and that subsequent lookups hit the cache. """ assert mock_lookup.call_count == 2 assert app.env.issuetracker_cache == {'10': issue, '11': None} sphinxcontrib-issuetracker-0.11/tests/test_pypi.py0000644000076500000240000000507112075754566023571 0ustar swiesnerstaff00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2011, 2012 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_pypi ========= Test that this package is handled correctly on the cheeseshop. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import os import sys from subprocess import Popen, PIPE, CalledProcessError import pytest @pytest.mark.skipif(str('sys.version_info[0] > 2')) def test_description_rendering(): """ If this test raises any exception ReST rendering on PyPI will fail. It calls out to the renderer via subprocess to get a clean docutils import. Sphinx heavily monkey-patches some docutils internals, which causes different rendering and sometimes even exceptions. """ test_directory = os.path.abspath(os.path.dirname(__file__)) source_directory = os.path.abspath(os.path.join(test_directory, os.pardir)) pypi = os.path.join(test_directory, 'pypi.py') readme = os.path.join(source_directory, 'README.rst') cmd = [sys.executable, pypi, readme] proc = Popen(cmd, stdout=PIPE) stdout = proc.communicate()[0] if proc.returncode != 0: raise CalledProcessError(proc.returncode, cmd) assert stdout sphinxcontrib-issuetracker-0.11/tests/test_resolval.py0000644000076500000240000000756411773072133024433 0ustar swiesnerstaff00000000000000# -*- 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_resolval ============= Test resolval of pending issue references. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from docutils import nodes def pytest_funcarg__app(request): """ Adds the ``mock_lookup`` marker to the current test before creating the ``app``. """ request.applymarker(pytest.mark.mock_lookup) return request.getfuncargvalue('app') @pytest.mark.with_content('#10') def test_no_issue(resolved_doctree): """ Test that no reference is created if an issue could not be resolved. """ assert not resolved_doctree.next_node(nodes.reference) @pytest.mark.with_issue(id='10', title='Spam', url='spam', closed=False) def test_open_issue(app, resolved_doctree, issue): """ Test resolval of an open issue. """ assert app.env.issuetracker_cache == {'10': issue} pytest.assert_issue_xref(resolved_doctree, issue, '#10') @pytest.mark.with_issue(id='10', title='Eggs', url='eggs', closed=True) def test_closed_issue(resolved_doctree, issue): """ Test resolval of a closed issue. """ pytest.assert_issue_xref(resolved_doctree, issue, '#10') @pytest.mark.with_issue(id='10', title=None, url='eggs', closed=True) def test_issue_without_title(resolved_doctree, issue): """ Test resolval of issues without title. """ pytest.assert_issue_xref(resolved_doctree, issue, '#10') @pytest.mark.with_issue(id='10', title='Eggs', url='eggs', closed=True) @pytest.mark.with_content(':issue:`{issue.title} (#{issue.id})<10>`') def test_with_formatted_title(resolved_doctree, issue): """ Test issue with format in title. """ pytest.assert_issue_xref(resolved_doctree, issue, 'Eggs (#10)') @pytest.mark.with_issue(id='10', title=None, url='eggs', closed=True) @pytest.mark.with_content(':issue:`{{issue.title}} (#{issue.id}) <10>`') def test_with_escaped_formatted_title(resolved_doctree, issue): """ Test issue with escaped formats in title. """ pytest.assert_issue_xref(resolved_doctree, issue, '{issue.title} (#10)') @pytest.mark.with_issue(id='10', title='öäüß', url='eggs', closed=True) @pytest.mark.with_content(':issue:`{issue.title} <10>`') def test_non_ascii_title(resolved_doctree, issue): """ Test issues with non-ascii titles. """ pytest.assert_issue_xref(resolved_doctree, issue, 'öäüß') assert resolved_doctree.astext() == 'öäüß' sphinxcontrib-issuetracker-0.11/tests/test_role.py0000644000076500000240000000506311773072133023535 0ustar swiesnerstaff00000000000000# -*- 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_role ========= Test the ``issue`` role. .. moduleauthor:: Sebastian Wiesner """ 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') @pytest.mark.with_content(':issue:`10`') def test_simple(doctree, issue): """ Test simple usage of the role. """ pytest.assert_issue_pending_xref(doctree, '10', '10') @pytest.mark.with_content(':issue:`foo <10>`') def test_with_title(doctree, issue): """ Test role with an explicit title. """ pytest.assert_issue_pending_xref(doctree, '10', 'foo') @pytest.mark.confoverrides(issuetracker_plaintext_issues=False) @pytest.mark.with_content(':issue:`10` #10') def test_without_plaintext_issues(doctree, issue): """ Test that the role still works even if plaintext issues are disabled. """ pytest.assert_issue_pending_xref(doctree, '10', '10') sphinxcontrib-issuetracker-0.11/tests/test_setup.py0000644000076500000240000000732511773072133023737 0ustar swiesnerstaff00000000000000# -*- 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_setup ========== Test the setup procedure of the extension, make sure that everything is installed at the proper place after the extension setup finished. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import re BUILTIN_TRACKER_NAME_PATTERN = re.compile('lookup_(.*)_issue') import pytest from sphinx.environment import SphinxStandaloneReader from sphinxcontrib import issuetracker from sphinxcontrib.issuetracker import resolvers 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(resolvers.BUILTIN_ISSUE_TRACKERS) for attr in dir(resolvers): 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. """ assert issuetracker.IssueReferences in SphinxStandaloneReader.transforms @pytest.mark.confoverrides(issuetracker_plaintext_issues=False) def test_transform_not_added(app): """ Test that the transformer is not added if transformations are disabled. """ transforms = SphinxStandaloneReader.transforms assert issuetracker.IssueReferences not in transforms sphinxcontrib-issuetracker-0.11/tests/test_stylesheet.py0000644000076500000240000001062711773072133024767 0ustar swiesnerstaff00000000000000# -*- 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_stylesheet =============== Test application of the CSS stylesheet to the HTML output. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import sys import pytest if sys.version_info[0] < 3: sip = pytest.importorskip('sip') sip.setapi('QString', 2) text_type = unicode else: text_type = str QtCore = pytest.importorskip('PyQt4.QtCore') QtGui = pytest.importorskip('PyQt4.QtGui') QtWebKit = pytest.importorskip('PyQt4.QtWebKit') # Qt application setup and rendering takes time, these tests are slow pytestmark = pytest.mark.slow def pytest_funcarg__app(request): """ Application with mocked lookup. """ request.applymarker(pytest.mark.mock_lookup) return request.getfuncargvalue('app') def pytest_funcarg__qt_app(request): """ A QApplication to drive rendering tests. """ return request.cached_setup(lambda: QtGui.QApplication([]), scope='module') def pytest_funcarg__web_page(request): """ Return a web page object for the rendered ``content``. """ request.getfuncargvalue('qt_app') # keep the web page alive during execution of the current test. Prevents # the python GC from cleaning the web_page object at the end of this # function and thus prevents segfaults when accessing elements in the page. web_page = request.cached_setup(QtWebKit.QWebPage, scope='function') main_frame = web_page.mainFrame() index_html_file = request.getfuncargvalue('index_html_file') # wait for "loadFinished" signal to make sure that the whole content is # parsed before we run the test. see # http://www.developer.nokia.com/Community/Wiki/How_to_wait_synchronously_for_a_Signal_in_Qt loop = QtCore.QEventLoop() main_frame.loadFinished.connect(loop.quit) main_frame.load(QtCore.QUrl(text_type(index_html_file))) loop.exec_() return web_page def pytest_funcarg__reference(request): """ Return the issue reference element in the ``main_frame``. """ web_page = request.getfuncargvalue('web_page') issue_element = web_page.mainFrame().findFirstElement('.xref.issue') if issue_element.isNull(): raise ValueError('null element') return issue_element def pytest_funcarg__text_decoration(request): """ Return the ``text-decoration`` style property of the ``reference`` element. """ reference = request.getfuncargvalue('reference') resolve_strategy = QtWebKit.QWebElement.CascadedStyle return reference.styleProperty('text-decoration', resolve_strategy) @pytest.mark.with_issue(id='10', title='Eggs', closed=False, url='eggs') def test_open_issue(text_decoration): """ Test that an open issue is not struck through. """ assert not text_decoration @pytest.mark.with_issue(id='10', title='Eggs', closed=True, url='eggs') def test_closed_issue(text_decoration): """ Test that a closed issue is struck through. """ assert text_decoration == 'line-through' sphinxcontrib-issuetracker-0.11/tests/test_tracker_config.py0000644000076500000240000001065611773072133025560 0ustar swiesnerstaff00000000000000# -*- 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_tracker_config =================== Test the TrackerConfig class. .. moduleauthor:: Sebastian Wiesner """ 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.11/tests/test_transformer.py0000644000076500000240000001272011773072133025134 0ustar swiesnerstaff00000000000000# -*- 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_transformer ================ Test transforming of textual issue references. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from docutils import nodes from sphinx.addnodes import pending_xref 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') @pytest.mark.confoverrides(issuetracker_plaintext_issues=False) @pytest.mark.with_content('#10') def test_transform_disabled(doctree): """ Test that no reference is inserted if transforming is disabled. """ assert not doctree.next_node(pending_xref) @pytest.mark.with_content('#10') def test_transform_simple(doctree): """ Test a simple transform with just an issue id. """ pytest.assert_issue_pending_xref(doctree, '10', '#10') @pytest.mark.with_content('#10') @pytest.mark.confoverrides( issuetracker_title_template='{issue.title} (#{issue.id})') def test_transform_with_title_template(doctree): """ Test transformation with title template. """ pytest.assert_issue_pending_xref( doctree, '10', '{issue.title} (#{issue.id})') @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. """ pytest.assert_issue_pending_xref(doctree, '10', '#10') assert doctree.astext() == content @pytest.mark.with_content('äöü #10 ß') def test_transform_non_ascii(doctree, content): """ Test transformation with non-ascii characters. """ pytest.assert_issue_pending_xref(doctree, '10', '#10') assert doctree.astext() == content @pytest.mark.with_content('*#10* **#10**') def test_transform_inline_markup(doctree): """ Test that issue ids inside inline markup like emphasis are transformed. """ emphasis = doctree.next_node(nodes.emphasis) assert emphasis pytest.assert_issue_pending_xref(emphasis, '10', '#10') strong = doctree.next_node(nodes.strong) assert strong pytest.assert_issue_pending_xref(strong, '10', '#10') @pytest.mark.with_content('``#10``') def test_transform_literal(doctree): """ Test that transformation leaves literals untouched. """ assert not doctree.next_node(pending_xref) literal = doctree.next_node(nodes.literal) assert literal assert literal.astext() == '#10' @pytest.mark.with_content("""\ spam:: eggs #10""") def test_transform_literal_block(doctree): """ Test that transformation leaves literal blocks untouched. """ assert not doctree.next_node(pending_xref) literal_block = doctree.next_node(nodes.literal_block) assert literal_block assert literal_block.astext() == 'eggs\n #10' @pytest.mark.with_content("""\ .. code-block:: python eggs('#10')""") def test_transform_code_block(doctree): assert not doctree.next_node(pending_xref) literal_block = doctree.next_node(nodes.literal_block) assert literal_block assert literal_block.astext() == "eggs('#10')" assert literal_block['language'] == 'python' @pytest.mark.with_content("""\ foo: >>> print('#10')""") def test_transform_doctest_block(doctree): assert not doctree.next_node(pending_xref) doctest_block = doctree.next_node(nodes.doctest_block) assert doctest_block assert doctest_block.astext() == ">>> print('#10')" @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(('a', 'b'))) sphinxcontrib-issuetracker-0.11/tox.ini0000644000076500000240000000165412075752237021344 0ustar swiesnerstaff00000000000000[tox] envlist=py26,py27,py32,pypy [testenv] deps= sphinx>=1.1 requests>=1.1 mock>=0.7 pytest>=2.0 ; These are not yet compatible to Python 3 ; launchpadlib ; SOAPpy>=0.12.5 ; python-debianbts>=1.10 commands= py.test {posargs:--junitxml={envname}-tests.xml} [testenv:py26] deps= sphinx>=1.1 requests>=1.1 mock>=0.7 pytest>=2.0 launchpadlib SOAPpy>=0.12.5 python-debianbts>=1.10 commands= py.test {posargs:--junitxml={envname}-tests.xml} [testenv:py27] deps= sphinx>=1.1 requests>=1.1 mock>=0.7 pytest>=2.0 launchpadlib SOAPpy>=0.12.5 python-debianbts>=1.10 commands= py.test {posargs:--junitxml={envname}-tests.xml} [testenv:doc] basepython=python2 deps= sphinx>=1.1 commands= sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html