diff options
211 files changed, 20881 insertions, 6546 deletions
@@ -1,7 +1,10 @@ .*\.pyc .*\.egg +.*\.so build/ dist/ +tests/.coverage +sphinx/pycode/Grammar.*pickle Sphinx.egg-info/ doc/_build/ TAGS @@ -6,13 +6,20 @@ Substantial parts of the templates were written by Armin Ronacher Other contributors, listed alphabetically, are: * Daniel Bültmann -- todo extension +* Michael Droettboom -- inheritance_diagram extension +* Charles Duffy -- original graphviz extension * Josip Dzolonga -- coverage builder * Horst Gutmann -- internationalization support * Martin Hans -- autodoc improvements * Dave Kuhlman -- original LaTeX writer * Thomas Lamb -- linkcheck builder +* Will Maier -- directory HTML builder +* Christopher Perkins -- autosummary integration * Benjamin Peterson -- unittests -* Pauli Virtanen -- autodoc improvements +* Stefan Seefeld -- toctree improvements +* Antonio Valentino -- qthelp builder +* Pauli Virtanen -- autodoc improvements, autosummary extension +* Stefan van der Walt -- autosummary extension * Sebastian Wiesner -- image handling, distutils support Many thanks for all contributions! @@ -1,3 +1,205 @@ +Release 0.6 (in development) +============================ + +New features added +------------------ + +* Incompatible changes: + + - Templating now requires the Jinja2 library, which is an enhanced + version of the old Jinja1 engine. Since the syntax and semantic + is largely the same, very few fixes should be necessary in + custom templates. + + - The "document" div tag has been moved out of the ``layout.html`` + template's "document" block, because the closing tag was already + outside. If you overwrite this block, you need to remove your + "document" div tag as well. + + - The ``autodoc_skip_member`` event now also gets to decide + whether to skip members whose name starts with underscores. + Previously, these members were always automatically skipped. + Therefore, if you handle this event, add something like this + to your event handler to restore the old behavior:: + + if name.startswith('_'): + return True + +* Theming support, see the new section in the documentation. + +* Markup: + + - Due to popular demand, added a ``:doc:`` role which directly + links to another document without the need of creating a + label to which a ``:ref:`` could link to. + + - #4: Added a ``:download:`` role that marks a non-document file + for inclusion into the HTML output and links to it. + + - Added an ``only`` directive that can selectively include text + based on enabled "tags". Tags can be given on the command + line. Also, the current builder output format (e.g. "html" or + "latex") is always a defined tag. + + - #10: Added HTML section numbers, enabled by giving a + ``:numbered:`` flag to the ``toctree`` directive. + + - #114: Added an ``abbr`` role to markup abbreviations and + acronyms. + + - The ``literalinclude`` directive now supports several more + options, to include only parts of a file. + + - The ``toctree`` directive now supports a ``:hidden:`` flag, + which will prevent links from being generated in place of + the directive -- this allows you to define your document + structure, but place the links yourself. + + - #123: The ``glossary`` directive now supports a ``:sorted:`` + flag that sorts glossary entries alphabetically. + + - Paths to images, literal include files and download files + can now be absolute (like ``/images/foo.png``). They are + treated as relative to the top source directory. + + - #52: There is now a ``hlist`` directive, creating a compact + list by placing distributing items into multiple columns. + + - #77: If a description environment with info field list only + contains one ``:param:`` entry, no bullet list is generated. + + - #6: Don't generate redundant ``<ul>`` for top-level TOC tree + items, which leads to a visual separation of TOC entries. + + - #23: Added a ``classmethod`` directive along with ``method`` + and ``staticmethod``. + + - Scaled images now get a link to the unscaled version. + + - SVG images are now supported in HTML (via ``<object>`` and + ``<embed>`` tags). + + - Added a ``toctree`` callable to the templates, and the ability + to include external links in toctrees. The 'collapse' keyword + argument indicates whether or not to only display subitems of + the current page. (Defaults to True.) + +* Configuration: + + - The new config value ``rst_epilog`` can contain reST that is + appended to each source file that is read. This is the right + place for global substitutions. + + - The new ``html_add_permalinks`` config value can be used to + switch off the generated "paragraph sign" permalinks for each + heading and definition environment. + + - The new ``html_show_sourcelink`` config value can be used to + switch off the links to the reST sources in the sidebar. + + - The default value for ``htmlhelp_basename`` is now the project + title, cleaned up as a filename. + + - The new ``modindex_common_prefix`` config value can be used to + ignore certain package names for module index sorting. + + - The new ``trim_footnote_reference_space`` config value mirrors + the docutils config value of the same name and removes the + space before a footnote reference that is necessary for reST + to recognize the reference. + + - The new ``latex_additional_files`` config value can be used to + copy files (that Sphinx doesn't copy automatically, e.g. if they + are referenced in custom LaTeX added in ``latex_elements``) to + the build directory. + +* Builders: + + - The HTML builder now stores a small file named ``.buildinfo`` in + its output directory. It stores a hash of config values that + can be used to determine if a full rebuild needs to be done (e.g. + after changing ``html_theme``). + + - New builder for Qt help collections, by Antonio Valentino. + + - The new ``DirectoryHTMLBuilder`` (short name ``dirhtml``) creates + a separate directory for every page, and places the page there + in a file called ``index.html``. Therefore, page URLs and links + don't need to contain ``.html``. + + - The new ``html_link_suffix`` config value can be used to select + the suffix of generated links between HTML files. + + - #96: The LaTeX builder now supports figures wrapped by text, when + using the ``figwidth`` option and right/left alignment. + +* New translations: + + - Italian by Sandro Dentella. + + - Ukrainian by Petro Sasnyk. + + - Finnish by Jukka Inkeri. + +* Extensions and API: + + - New ``graphviz`` extension to embed graphviz graphs. + + - New ``inheritance_diagram`` extension to embed... inheritance + diagrams! + + - New ``autosummary`` extension that generates summaries of + modules and automatic documentation of modules. + + - Autodoc now has a reusable Python API, which can be used to + create custom types of objects to auto-document (e.g. Zope + interfaces). See also ``Sphinx.add_autodocumenter()``. + + - Autodoc now handles documented attributes. + + - Autodoc now handles inner classes and their methods. + + - Autodoc can document classes as functions now if explicitly + marked with `autofunction`. + + - Autodoc can now exclude single members from documentation + via the ``exclude-members`` option. + + - Autodoc can now order members either alphabetically (like + previously) or by member type; configurable either with the + config value ``autodoc_member_order`` or a ``member-order`` + option per directive. + + - The function ``Sphinx.add_directive()`` now also supports + docutils 0.5-style directive classes. If they inherit from + ``sphinx.util.compat.Directive``, they also work with + docutils 0.4. + + - There is now a ``Sphinx.add_lexer()`` method to be able to use + custom Pygments lexers easily. + + - There is now ``Sphinx.add_generic_role()`` to mirror the + docutils' own function. + +* Other changes: + + - Config overrides for single dict keys can now be given on the + command line. + + - There is now a ``doctest_global_setup`` config value that can + be used to give setup code for all doctests in the documentation. + + - Source links in HTML are now generated with ``rel="nofollow"``. + + - Quickstart can now generate a Windows ``make.bat`` file. + + - #62: There is now a ``-w`` option for sphinx-build that writes + warnings to a file, in addition to stderr. + + - There is now a ``-W`` option for sphinx-build that turns warnings + into errors. + + Release 0.5.2 (in development) ============================== @@ -7,38 +7,66 @@ included, please mail to `the Google group <http://groups.google.com/group/sphinx-dev>`_. * APSW: http://apsw.googlecode.com/svn/publish/index.html +* boostmpi: http://documen.tician.de/boostmpi/ * Calibre: http://calibre.kovidgoyal.net/user_manual/ * Chaco: http://code.enthought.com/projects/chaco/docs/html/ +* CodePy: http://documen.tician.de/codepy/ * Cython: http://docs.cython.org/ +* C\\C++ Python language binding project: http://language-binding.net/index.html * Director: http://packages.python.org/director/ * Django: http://docs.djangoproject.com/ * F2py: http://www.f2py.org/html/ * GeoDjango: http://geodjango.org/docs/ * Glashammer: http://glashammer.org/ * Grok: http://grok.zope.org/doc/current/ +* Hedge: http://documen.tician.de/hedge/ * IFM: http://fluffybunny.memebot.com/ifm-docs/index.html * Jinja: http://jinja.pocoo.org/2/documentation/ +* MapServer: http://mapserver.org/ * Matplotlib: http://matplotlib.sourceforge.net/ * Mayavi: http://code.enthought.com/projects/mayavi/docs/development/html/mayavi +* MeshPy: http://documen.tician.de/meshpy/ * Mixin.com: http://dev.mixin.com/ +* mpmath: http://mpmath.googlecode.com/svn/trunk/doc/build/index.html +* MyHDL: http://www.myhdl.org/doc/0.6/ * NetworkX: http://networkx.lanl.gov/ * NumPy: http://docs.scipy.org/doc/numpy/reference/ * ObjectListView: http://objectlistview.sourceforge.net/python +* OpenLayers: http://docs.openlayers.org/ +* openWNS: http://docs.openwns.org/ * Paste: http://pythonpaste.org/script/ * Paver: http://www.blueskyonmars.com/projects/paver/ * Py on Windows: http://timgolden.me.uk/python-on-windows/ +* PyCuda: http://documen.tician.de/pycuda/ * PyEphem: http://rhodesmill.org/pyephem/ -* PyPubSub: http://pubsub.sourceforge.net/ -* PyUblas: http://tiker.net/doc/pyublas/ +* Pyevolve: http://pyevolve.sourceforge.net/ +* PyLit: http://pylit.berlios.de/ +* Pylo: http://documen.tician.de/pylo/ * Pylons: http://docs.pylonshq.com/ +* PyMOTW: http://www.doughellmann.com/PyMOTW/ +* PyPubSub: http://pubsub.sourceforge.net/ +* pyrticle: http://documen.tician.de/pyrticle/ * Pysparse: http://pysparse.sourceforge.net/ -* Python: http://docs.python.org/dev/ +* Python: http://docs.python.org/ +* python-apt: http://people.debian.org/~jak/python-apt-doc/ +* PyUblas: http://documen.tician.de/pyublas/ +* Quex: http://quex.sourceforge.net/ +* Reteisi: http://docs.argolinux.org/reteisi/ +* Roundup: http://www.roundup-tracker.org/ +* Sage: http://sagemath.org/doc/ * Satchmo: http://www.satchmoproject.com/docs/svn/ +* Scapy: http://www.secdev.org/projects/scapy/doc/ +* Self: http://selflanguage.org/ +* SimPy: http://simpy.sourceforge.net/ * Sphinx: http://sphinx.pocoo.org/ +* Sprox: http://sprox.org/ * SQLAlchemy: http://www.sqlalchemy.org/docs/ +* Sqlkit: http://sqlkit.argolinux.org/ * SymPy: http://docs.sympy.org/ * tinyTiM: http://tinytim.sourceforge.net/docs/2.0/ * TurboGears: http://turbogears.org/2.0/docs/ +* VOR: http://www.vor-cycling.be/ +* WFront: http://discorporate.us/projects/WFront/ +* WTForms: http://wtforms.simplecodes.com/docs/ * Zope 3: e.g. http://docs.carduner.net/z3c-tutorial/ -* mpmath: http://mpmath.googlecode.com/svn/trunk/doc/build/index.html * zc.async: http://packages.python.org/zc.async/1.5.0/ @@ -1,9 +1,9 @@ -Copyright (c) 2007-2009 by the respective authors (see AUTHORS file). -All rights reserved. - License for Sphinx ================== +Copyright (c) 2007-2009 by the Sphinx team (see AUTHORS file). +All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -31,6 +31,64 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Licenses for incorporated software ================================== +The pgen2 package, included in this distribution under the name +sphinx.pycode.pgen2, is available in the Python 2.6 distribution under +the PSF license agreement for Python: + +---------------------------------------------------------------------- +Copyright © 2001-2008 Python Software Foundation; All Rights Reserved. + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation + ("PSF"), and the Individual or Organization ("Licensee") accessing + and otherwise using Python 2.6 software in source or binary form + and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF + hereby grants Licensee a nonexclusive, royalty-free, world-wide + license to reproduce, analyze, test, perform and/or display + publicly, prepare derivative works, distribute, and otherwise use + Python 2.6 alone or in any derivative version, provided, however, + that PSF's License Agreement and PSF's notice of copyright, i.e., + "Copyright © 2001-2008 Python Software Foundation; All Rights + Reserved" are retained in Python 2.6 alone or in any derivative + version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on + or incorporates Python 2.6 or any part thereof, and wants to make + the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary + of the changes made to Python 2.6. + +4. PSF is making Python 2.6 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY + WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY + REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY + PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.6 WILL NOT INFRINGE + ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + 2.6 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS + AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON + 2.6, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY + THEREOF. + +6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between PSF + and Licensee. This License Agreement does not grant permission to + use PSF trademarks or trade name in a trademark sense to endorse or + promote products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python 2.6, Licensee + agrees to be bound by the terms and conditions of this License + Agreement. +---------------------------------------------------------------------- + The included smartypants module, included as sphinx.util.smartypants, is available under the following license: @@ -133,3 +191,29 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---------------------------------------------------------------------- + +The included JQuery JavaScript library is available under the MIT +license: + +---------------------------------------------------------------------- +Copyright (c) 2008 John Resig, http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- diff --git a/MANIFEST.in b/MANIFEST.in index d87a881e..e1f0b575 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,11 +12,11 @@ include sphinx-build.py include sphinx-quickstart.py recursive-include sphinx/texinputs * -recursive-include sphinx/templates *.html *.xml -recursive-include sphinx/static * +recursive-include sphinx/themes * recursive-include sphinx/locale * recursive-include tests * recursive-include utils * +include sphinx/pycode/Grammar.txt recursive-include doc * prune doc/_build @@ -7,7 +7,9 @@ export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx all: clean-pyc check test check: - @$(PYTHON) utils/check_sources.py -i sphinx/style/jquery.js sphinx + @$(PYTHON) utils/check_sources.py -i sphinx/style/jquery.js \ + -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \ + -i doc/_build -i ez_setup.py -i tests/path.py . clean: clean-pyc clean-patchfiles @@ -28,3 +30,6 @@ reindent: test: @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) + +covertest: + @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST) @@ -1,3 +1,5 @@ +.. -*- restructuredtext -*- + ================= README for Sphinx ================= @@ -1,6 +1,4 @@ -[extractors] -jinja = sphinx._jinja.babel_extract [python: **.py] -[jinja: **/templates/**.html] -[jinja: **/templates/**.xml] +[jinja2: **/templates/**.html] +[jinja2: **/templates/**.xml] [javascript: **.js] diff --git a/doc/Makefile b/doc/Makefile index cb5fc645..07cee744 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -11,12 +11,13 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) . -.PHONY: help clean html web htmlhelp latex changes linkcheck +.PHONY: help clean html dirhtml pickle htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make <target>' where <target> is one of" @echo " html to make standalone HTML files" - @echo " web to make files usable by Sphinx.web" + @echo " dirhtml to make HTML files called index.html in directories" + @echo " pickle to make pickle files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @@ -31,13 +32,21 @@ html: @echo @echo "Build finished. The HTML pages are in _build/html." +dirhtml: + mkdir -p _build/dirhtml _build/doctrees + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml + @echo + @echo "Build finished. The HTML pages are in _build/dirhtml." + +text: + mkdir -p _build/text _build/doctrees + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text + @echo + @echo "Build finished." + pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle - @echo - @echo "Build finished; now you can run" - @echo " python -m sphinx.web _build/pickle" - @echo "to start the server." htmlhelp: mkdir -p _build/htmlhelp _build/doctrees @@ -46,6 +55,16 @@ htmlhelp: @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." +qthelp: + mkdir -p _build/qthelp _build/doctrees + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp + @echo + @echo "Build finished; now you can run qcollectiongenerator with the" \ + ".qhcp project file in build/qthelp." + @echo "# qcollectiongenerator _build/qthelp/Sphinx.qhcp" + @echo "To view the help collection:" + @echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc" + latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex diff --git a/doc/_templates/index.html b/doc/_templates/index.html index 43d07e82..1c399f92 100644 --- a/doc/_templates/index.html +++ b/doc/_templates/index.html @@ -81,5 +81,5 @@ </p> <p>The code can be found in a Mercurial repository, at <tt>http://bitbucket.org/birkenfeld/sphinx/</tt>.</p> - + {% endblock %} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html index 2cc15450..080c0935 100644 --- a/doc/_templates/layout.html +++ b/doc/_templates/layout.html @@ -1,17 +1,12 @@ {% extends "!layout.html" %} {% block rootrellink %} - <li><a href="{{ pathto('index') }}">Sphinx home </a> | </li> - <li><a href="{{ pathto('contents') }}">Documentation </a> »</li> + <li><a href="{{ pathto('index') }}">Sphinx home</a> | </li> + <li><a href="{{ pathto('contents') }}">Documentation</a>»</li> {% endblock %} -{% block relbar1 %} +{% block header %} <div style="background-color: white; text-align: left; padding: 10px 10px 15px 15px"> <img src="{{ pathto("_static/sphinx.png", 1) }}" alt="Sphinx logo" /> </div> -{{ super() }} {% endblock %} - -{# put the sidebar before the body #} -{% block sidebar1 %}{{ sidebar() }}{% endblock %} -{% block sidebar2 %}{% endblock %} diff --git a/doc/builders.rst b/doc/builders.rst index 508ab3c5..bee0094c 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -3,7 +3,7 @@ Available builders ================== -.. module:: sphinx.builder +.. module:: sphinx.builders :synopsis: Available built-in builder classes. These are the built-in Sphinx builders. More builders can be added by @@ -13,6 +13,7 @@ The builder's "name" must be given to the **-b** command-line option of :program:`sphinx-build` to select a builder. +.. module:: sphinx.builders.html .. class:: StandaloneHTMLBuilder This is the standard HTML builder. Its output is a directory with HTML @@ -22,14 +23,28 @@ The builder's "name" must be given to the **-b** command-line option of Its name is ``html``. +.. class:: DirectoryHTMLBuilder + + This is a subclass of the standard HTML builder. Its output is a directory + with HTML files, where each file is called ``index.html`` and placed in a + subdirectory named like its page name. For example, the document + ``markup/rest.rst`` will not result in an output file ``markup/rest.html``, + but ``markup/rest/index.html``. When generating links between pages, the + ``index.html`` is omitted, so that the URL would look like ``markup/rest/``. + + Its name is ``dirhtml``. + + .. versionadded:: 0.6 + .. class:: HTMLHelpBuilder This builder produces the same output as the standalone HTML builder, but also generates HTML Help support files that allow the Microsoft HTML Help Workshop to compile them into a CHM file. - Its name is ``htmlhelp``. + Its name is ``htmlhelp``. +.. module:: sphinx.builders.latex .. class:: LaTeXBuilder This builder produces a bunch of LaTeX files in the output directory. You @@ -50,6 +65,7 @@ The builder's "name" must be given to the **-b** command-line option of Its name is ``latex``. +.. module:: sphinx.builders.text .. class:: TextBuilder This builder produces a text file for each reST file -- this is almost the @@ -60,6 +76,7 @@ The builder's "name" must be given to the **-b** command-line option of .. versionadded:: 0.4 +.. currentmodule:: sphinx.builders.html .. class:: SerializingHTMLBuilder This builder uses a module that implements the Python serialization API @@ -81,7 +98,7 @@ The builder's "name" must be given to the **-b** command-line option of .. _PHP serialization: http://pypi.python.org/pypi/phpserialize .. attribute:: implementation - + A module that implements `dump()`, `load()`, `dumps()` and `loads()` functions that conform to the functions with the same names from the pickle module. Known modules implementing this interface are @@ -135,6 +152,7 @@ The builder's "name" must be given to the **-b** command-line option of .. versionadded:: 0.5 +.. module:: sphinx.builders.changes .. class:: ChangesBuilder This builder produces an HTML overview of all :dir:`versionadded`, @@ -144,6 +162,7 @@ The builder's "name" must be given to the **-b** command-line option of Its name is ``changes``. +.. module:: sphinx.builders.linkcheck .. class:: CheckExternalLinksBuilder This builder scans all documents for external links, tries to open them with diff --git a/doc/concepts.rst b/doc/concepts.rst index ca7aaf7c..e6d5fa02 100644 --- a/doc/concepts.rst +++ b/doc/concepts.rst @@ -16,6 +16,9 @@ directory`, the extension is stripped, and path separators are converted to slashes. All values, parameters and suchlike referring to "documents" expect such a document name. +Examples for document names are ``index``, ``library/zipfile``, or +``reference/datamodel/types``. Note that there is no leading slash. + The TOC tree ------------ @@ -55,22 +58,37 @@ tables of contents. The ``toctree`` directive is the central element. ``strings`` and so forth, and it knows that they are children of the shown document, the library index. From this information it generates "next chapter", "previous chapter" and "parent chapter" links. - + Document titles in the :dir:`toctree` will be automatically read from the - title of the referenced document. If that isn't what you want, you can give - the specify an explicit title and target using a similar syntax to reST + title of the referenced document. If that isn't what you want, you can + specify an explicit title and target using a similar syntax to reST hyperlinks (and Sphinx's :ref:`cross-referencing syntax <xref-syntax>`). This looks like:: - + .. toctree:: - + intro All about strings <strings> datatypes - + The second line above will link to the ``strings`` document, but will use the title "All about strings" instead of the title of the ``strings`` document. + You can also add external links, by giving an HTTP URL instead of a document + name. + + If you want to have section numbers even in HTML output, give the toctree a + ``numbered`` flag option. For example:: + + .. toctree:: + :numbered: + + foo + bar + + Numbering then starts at the heading of ``foo``. Sub-toctrees are + automatically numbered (don't give the ``numbered`` flag to those). + You can use "globbing" in toctree directives, by giving the ``glob`` flag option. All entries are then matched against the list of available documents, and matches are inserted into the list alphabetically. Example:: @@ -85,7 +103,24 @@ tables of contents. The ``toctree`` directive is the central element. This includes first all documents whose names start with ``intro``, then all documents in the ``recipe`` folder, then all remaining documents (except the one containing the directive, of course.) [#]_ - + + The special entry name ``self`` stands for the document containing the + toctree directive. This is useful if you want to generate a "sitemap" from + the toctree. + + You can also give a "hidden" option to the directive, like this:: + + .. toctree:: + :hidden: + + doc_1 + doc_2 + + This will still notify Sphinx of the document hierarchy, but not insert links + into the document at the location of the directive -- this makes sense if you + intend to insert these links yourself, in a different style, or in the HTML + sidebar. + In the end, all documents in the :term:`source directory` (or subdirectories) must occur in some ``toctree`` directive; Sphinx will emit a warning if it finds a file that is not included, because that means that this file will not @@ -100,6 +135,10 @@ tables of contents. The ``toctree`` directive is the central element. .. versionchanged:: 0.3 Added "globbing" option. + .. versionchanged:: 0.6 + Added "numbered" and "hidden" options as well as external links and + support for "self" references. + Special names ------------- diff --git a/doc/conf.py b/doc/conf.py index 89247821..e1a48aa2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,26 +1,9 @@ # -*- coding: utf-8 -*- # -# Sphinx documentation build configuration file, created by -# sphinx-quickstart.py on Sat Mar 8 21:47:50 2008. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). -# -# All configuration values have a default value; values that are commented out -# serve to show the default value. +# Sphinx documentation build configuration file import sys, os, re -# If your extensions are in another directory, add it here. -#sys.path.append(os.path.dirname(__file__)) - -# General configuration -# --------------------- - -language = 'en' - # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.addons.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo'] @@ -36,46 +19,22 @@ master_doc = 'contents' # General substitutions. project = 'Sphinx' -copyright = '2008, Georg Brandl' +copyright = '2007-2009, Georg Brandl' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. -# -# The short X.Y version. import sphinx version = sphinx.__released__ -# The full version, including alpha/beta/rc tags. release = version -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - +# Show author directives in the output. show_authors = True -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'friendly' - +# The HTML template theme. +html_theme = 'sphinxdoc' -# Options for HTML output -# ----------------------- - -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'sphinxdoc.css' +# A list of ignored prefixes names for module index sorting. +modindex_common_prefix = ['sphinx.'] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -86,10 +45,6 @@ html_static_path = ['_static'] # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - # Content template for the index page. html_index = 'index.html' @@ -100,50 +55,30 @@ html_sidebars = {'index': 'indexsidebar.html'} # templates. html_additional_pages = {'index': 'index.html'} -# If true, the reST sources are included in the HTML build as _sources/<name>. -#html_copy_source = True - +# Generate an OpenSearch description with that URL as the base. html_use_opensearch = 'http://sphinx.pocoo.org' # Output file base name for HTML help builder. htmlhelp_basename = 'Sphinxdoc' - -# Options for LaTeX output -# ------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). +# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation', 'Georg Brandl', 'manual', 1)] +# Add our logo to the LaTeX file. latex_logo = '_static/sphinx.png' -#latex_use_parts = True - # Additional stuff for the LaTeX preamble. latex_elements = { 'fontpkg': '\\usepackage{palatino}' } -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - - -# Extension options -# ----------------- - +# Put TODOs into the output. todo_include_todos = True -# Extension interface -# ------------------- +# -- Extension interface ------------------------------------------------------- from sphinx import addnodes @@ -190,7 +125,9 @@ def parse_event(env, sig, signode): def setup(app): from sphinx.ext.autodoc import cut_lines app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) - app.add_description_unit('directive', 'dir', 'pair: %s; directive', parse_directive) + app.add_description_unit('directive', 'dir', 'pair: %s; directive', + parse_directive) app.add_description_unit('role', 'role', 'pair: %s; role', parse_role) - app.add_description_unit('confval', 'confval', 'pair: %s; configuration value') + app.add_description_unit('confval', 'confval', + 'pair: %s; configuration value') app.add_description_unit('event', 'event', 'pair: %s; event', parse_event) diff --git a/doc/config.rst b/doc/config.rst index e6af9aae..54303bbb 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -22,8 +22,8 @@ Important points to note: * The term "fully-qualified name" refers to a string that names an importable Python object inside a module; for example, the FQN - ``"sphinx.builder.Builder"`` means the ``Builder`` class in the - ``sphinx.builder`` module. + ``"sphinx.builders.Builder"`` means the ``Builder`` class in the + ``sphinx.builders`` module. * Remember that document names use ``/`` as the path separator and don't contain the file name extension. @@ -38,6 +38,11 @@ Important points to note: delete them from the namespace with ``del`` if appropriate. Modules are removed automatically, so you don't need to ``del`` your imports after use. +* There is a special object named ``tags`` available in the config file. + It can be used to query and change the tags (see :ref:`tags`). Use + ``tags.has('tag')`` to query, ``tags.add('tag')`` and ``tags.remove('tag')`` + to change. + General configuration --------------------- @@ -76,7 +81,7 @@ General configuration .. versionadded:: 0.5 Previously, Sphinx accepted only UTF-8 encoded sources. - + .. confval:: master_doc The document name of the "master" document, that is, the document that @@ -131,18 +136,35 @@ General configuration .. confval:: templates_path A list of paths that contain extra templates (or templates that overwrite - builtin templates). Relative paths are taken as relative to the - configuration directory. + builtin/theme-specific templates). Relative paths are taken as relative to + the configuration directory. .. confval:: template_bridge A string with the fully-qualified name of a callable (or simply a class) that returns an instance of :class:`~sphinx.application.TemplateBridge`. This instance is then used to render HTML documents, and possibly the output of - other builders (currently the changes builder). + other builders (currently the changes builder). (Note that the template + bridge must be made theme-aware if HTML themes are to be used.) + +.. confval:: rst_epilog + + .. index:: pair: global; substitutions + + A string of reStructuredText that will be included at the end of every source + file that is read. This is the right place to add substitutions that should + be available in every file. An example:: + + rst_epilog = """ + .. |psf| replace:: Python Software Foundation + """ + + .. versionadded:: 0.6 .. confval:: default_role + .. index:: default; role + The name of a reST role (builtin or Sphinx extension) to use as the default role, that is, for text marked up ```like this```. This can be set to ``'obj'`` to make ```filter``` a cross-reference to the function "filter". @@ -164,9 +186,19 @@ General configuration .. versionadded:: 0.5 +.. confval:: modindex_common_prefix + + A list of prefixes that are ignored for sorting the module index (e.g., + if this is set to ``['foo.']``, then ``foo.bar`` is shown under ``B``, not + ``F``). This can be handy if you document a project that consists of a single + package. Works only for the HTML builder currently. Default is ``[]``. + + .. versionadded:: 0.6 + + Project information ------------------- - + .. confval:: project The documented project's name. @@ -204,11 +236,14 @@ Project information * ``de`` -- German * ``en`` -- English * ``es`` -- Spanish + * ``fi`` -- Finnish * ``fr`` -- French + * ``it`` -- Italian * ``nl`` -- Dutch * ``pl`` -- Polish * ``pt_BR`` -- Brazilian Portuguese * ``sl`` -- Slovenian + * ``uk_UA`` -- Ukrainian * ``zh_TW`` -- Traditional Chinese .. confval:: today @@ -232,7 +267,7 @@ Project information :ref:`code-examples` for more details. .. versionadded:: 0.5 - + .. confval:: pygments_style The style name to use for Pygments highlighting of source code. Default is @@ -260,6 +295,13 @@ Project information A boolean that decides whether :dir:`moduleauthor` and :dir:`sectionauthor` directives produce any output in the built files. +.. confval:: trim_footnote_reference_space + + Trim spaces before footnote references that are necessary for the reST parser + to recognize the footnote, but do not look too nice in the output. + + .. versionadded:: 0.6 + .. _html-options: @@ -269,6 +311,37 @@ Options for HTML output These options influence HTML as well as HTML Help output, and other builders that use Sphinx' HTMLWriter class. +.. confval:: html_theme + + The "theme" that the HTML output should use. See the :doc:`section about + theming <theming>`. The default is ``'default'``. + + .. versionadded:: 0.6 + +.. confval:: html_theme_options + + A dictionary of options that influence the look and feel of the selected + theme. These are theme-specific. For the options understood by the builtin + themes, see :ref:`this section <builtin-themes>`. + + .. versionadded:: 0.6 + +.. confval:: html_theme_path + + A list of paths that contain custom themes, either as subdirectories or as + zip files. Relative paths are taken as relative to the configuration + directory. + + .. versionadded:: 0.6 + +.. confval:: html_style + + The style sheet to use for HTML pages. A file of that name must exist either + in Sphinx' :file:`static/` path, or in one of the custom paths given in + :confval:`html_static_path`. Default is the stylesheet given by the selected + theme. If you only want to add or override a few things compared to the + theme's stylesheet, use CSS ``@import`` to import the theme's stylesheet. + .. confval:: html_title The "title" for HTML documentation generated with Sphinx' own templates. @@ -285,12 +358,6 @@ that use Sphinx' HTMLWriter class. .. versionadded:: 0.4 -.. confval:: html_style - - The style sheet to use for HTML pages. A file of that name must exist either - in Sphinx' :file:`static/` path, or in one of the custom paths given in - :confval:`html_static_path`. Default is ``'default.css'``. - .. confval:: html_logo If given, this must be the name of an image file that is the logo of the @@ -314,8 +381,8 @@ that use Sphinx' HTMLWriter class. A list of paths that contain custom static files (such as style sheets or script files). Relative paths are taken as relative to the configuration - directory. They are copied to the output directory after the builtin static - files, so a file named :file:`default.css` will overwrite the builtin + directory. They are copied to the output directory after the theme's static + files, so a file named :file:`default.css` will overwrite the theme's :file:`default.css`. .. versionchanged:: 0.4 @@ -332,6 +399,15 @@ that use Sphinx' HTMLWriter class. If true, *SmartyPants* will be used to convert quotes and dashes to typographically correct entities. Default: ``True``. +.. confval:: html_add_permalinks + + If true, Sphinx will add "permalinks" for each heading and description + environment as paragraph signs that become visible when the mouse hovers over + them. Default: ``True``. + + .. versionadded:: 0.6 + Previously, this was always activated. + .. confval:: html_sidebars Custom sidebar templates, must be a dictionary that maps document names to @@ -399,6 +475,13 @@ that use Sphinx' HTMLWriter class. will only display the titles of matching documents, and no excerpt from the matching contents. +.. confval:: html_show_sourcelink + + If true (and :confval:`html_copy_source` is true as well), links to the + reST sources will be added to the sidebar. The default is ``True``. + + .. versionadded:: 0.6 + .. confval:: html_use_opensearch If nonempty, an `OpenSearch <http://opensearch.org>` description file will be @@ -415,10 +498,18 @@ that use Sphinx' HTMLWriter class. .. versionadded:: 0.4 +.. confval:: html_link_suffix + + Suffix for generated links to HTML files. The default is whatever + :confval:`html_file_suffix` is set to; it can be set differently (e.g. to + support different web server setups). + + .. versionadded:: 0.6 + .. confval:: html_translator_class A string with the fully-qualified name of a HTML Translator class, that is, a - subclass of Sphinx' :class:`~sphinx.htmlwriter.HTMLTranslator`, that is used + subclass of Sphinx' :class:`~sphinx.writers.html.HTMLTranslator`, that is used to translate document trees to HTML. Default is ``None`` (use the builtin translator). @@ -502,7 +593,7 @@ These options influence LaTeX output. avoid interpretation as escape sequences. * Keys that you may want to override include: - + ``'papersize'`` Paper size option of the document class (``'a4paper'`` or ``'letterpaper'``), default ``'letterpaper'``. @@ -526,9 +617,9 @@ These options influence LaTeX output. Additional preamble content, default empty. ``'footer'``` Additional footer content (before the indices), default empty. - + * Keys that don't need be overridden unless in special cases are: - + ``'inputenc'`` "inputenc" package inclusion, default ``'\\usepackage[utf8]{inputenc}'``. @@ -545,9 +636,9 @@ These options influence LaTeX output. "printindex" call, the last thing in the file, default ``'\\printindex'``. Override if you want to generate the index differently or append some content after the index. - + * Keys that are set by other options and therefore should not be overridden are: - + ``'docclass'`` ``'classoptions'`` ``'title'`` @@ -560,7 +651,20 @@ These options influence LaTeX output. ``'makemodindex'`` ``'shorthandoff'`` ``'printmodindex'`` - + +.. confval:: latex_additional_files + + A list of file names, relative to the configuration directory, to copy to the + build directory when building LaTeX output. This is useful to copy files + that Sphinx doesn't copy automatically, e.g. if they are referenced in custom + LaTeX added in ``latex_elements``. Image files that are referenced in source + files (e.g. via ``.. image::``) are copied automatically. + + You have to make sure yourself that the filenames don't collide with those of + any automatically copied files. + + .. versionadded:: 0.6 + .. confval:: latex_preamble Additional LaTeX markup for the preamble. diff --git a/doc/contents.rst b/doc/contents.rst index 6ddbcbcb..1f3860ea 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -12,9 +12,11 @@ Sphinx documentation contents markup/index builders config + theming templating extensions - + + faq glossary changes examples diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index fcc29e38..6864d6ba 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -10,25 +10,39 @@ This function is called at initialization time with one argument, the application object representing the Sphinx process. This application object has the following public API: +.. method:: Sphinx.setup_extension(name) + + Load the extension given by the module *name*. Use this if your extension + needs the features provided by another extension. + .. method:: Sphinx.add_builder(builder) Register a new builder. *builder* must be a class that inherits from - :class:`~sphinx.builder.Builder`. + :class:`~sphinx.builders.Builder`. -.. method:: Sphinx.add_config_value(name, default, rebuild_env) +.. method:: Sphinx.add_config_value(name, default, rebuild) Register a configuration value. This is necessary for Sphinx to recognize new values and set default values accordingly. The *name* should be prefixed with the extension name, to avoid clashes. The *default* value can be any - Python object. The boolean value *rebuild_env* must be ``True`` if a change - in the setting only takes effect when a document is parsed -- this means that - the whole environment must be rebuilt. + Python object. The string value *rebuild* must be one of those values: + + * ``'env'`` if a change in the setting only takes effect when a document is + parsed -- this means that the whole environment must be rebuilt. + * ``'html'`` if a change in the setting needs a full rebuild of HTML + documents. + * ``''`` if a change in the setting will not need any special rebuild. .. versionchanged:: 0.4 If the *default* value is a callable, it will be called with the config object as its argument in order to get the default value. This can be used to implement config values whose default depends on other values. + .. versionchanged:: 0.6 + Changed *rebuild* from a simple boolean (equivalent to ``''`` or + ``'env'``) to a string. However, booleans are still accepted and + converted internally. + .. method:: Sphinx.add_event(name) Register an event called *name*. @@ -45,12 +59,12 @@ the following public API: :exc:`docutils.nodes.SkipNode`. Example:: class math(docutils.nodes.Element) - + def visit_math_html(self, node): self.body.append(self.starttag(node, 'math')) def depart_math_html(self, node): self.body.append('</math>') - + app.add_node(math, html=(visit_math_html, depart_math_html)) Obviously, translators for which you don't specify visitor methods will choke @@ -60,13 +74,28 @@ the following public API: Added the support for keyword arguments giving visit functions. .. method:: Sphinx.add_directive(name, func, content, arguments, **options) + Sphinx.add_directive(name, directiveclass) Register a Docutils directive. *name* must be the prospective directive - name, *func* the directive function for details about the signature and - return value. *content*, *arguments* and *options* are set as attributes on - the function and determine whether the directive has content, arguments and - options, respectively. For their exact meaning, please consult the Docutils - documentation. + name. There are two possible ways to write a directive: + + * In the docutils 0.4 style, *func* is the directive function. *content*, + *arguments* and *options* are set as attributes on the function and + determine whether the directive has content, arguments and options, + respectively. + + * In the docutils 0.5 style, *directiveclass* is the directive class. It + must already have attributes named *has_content*, *required_arguments*, + *optional_arguments*, *final_argument_whitespace* and *option_spec* that + correspond to the options for the function way. See `the Docutils docs + <http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_ for + details. + + The directive class normally must inherit from the class + ``docutils.parsers.rst.Directive``. When writing a directive for usage in + a Sphinx extension, you inherit from ``sphinx.util.compat.Directive`` + instead which does the right thing even on docutils 0.4 (which doesn't + support directive classes otherwise). For example, the (already existing) :dir:`literalinclude` directive would be added like this:: @@ -78,7 +107,8 @@ the following public API: language = direcitves.unchanged, encoding = directives.encoding) - .. XXX once we target docutils 0.5, update this + .. versionchanged:: 0.6 + Docutils 0.5-style directive classes are now supported. .. method:: Sphinx.add_role(name, role) @@ -86,6 +116,13 @@ the following public API: source, *role* the role function (see the `Docutils documentation <http://docutils.sourceforge.net/docs/howto/rst-roles.html>`_ on details). +.. method:: Sphinx.add_generic_role(name, nodeclass) + + Register a Docutils role that does nothing but wrap its contents in the + node given by *nodeclass*. + + .. versionadded:: 0.6 + .. method:: Sphinx.add_description_unit(directivename, rolename, indextemplate='', parse_node=None, ref_nodeclass=None) This method is a very convenient way to add a new type of information that @@ -167,7 +204,32 @@ the following public API: :confval:`the docs for the config value <html_static_path>`. .. versionadded:: 0.5 - + +.. method:: Sphinx.add_lexer(alias, lexer) + + Use *lexer*, which must be an instance of a Pygments lexer class, to + highlight code blocks with the given language *alias*. + + .. versionadded:: 0.6 + +.. method:: Sphinx.add_autodocumenter(cls) + + Add *cls* as a new documenter class for the :mod:`sphinx.ext.autodoc` + extension. It must be a subclass of :class:`sphinx.ext.autodoc.Documenter`. + This allows to auto-document new types of objects. See the source of the + autodoc module for examples on how to subclass :class:`Documenter`. + + .. versionadded:: 0.6 + +.. method:: Sphinx.add_autodoc_attrgetter(type, getter) + + Add *getter*, which must be a function with an interface compatible to the + :func:`getattr` builtin, as the autodoc attribute getter for objects that are + instances of *type*. All cases where autodoc needs to get an attribute of a + type are then handled by this function instead of :func:`getattr`. + + .. versionadded:: 0.6 + .. method:: Sphinx.connect(event, callback) Register *callback* to be called when *event* is emitted. For details on @@ -230,7 +292,7 @@ registered event handlers. since the module declarations could have been removed from the file. .. versionadded:: 0.5 - + .. event:: source-read (app, docname, source) Emitted when a source file has been read. The *source* argument is a list @@ -242,7 +304,7 @@ registered event handlers. ``:math:`...```. .. versionadded:: 0.5 - + .. event:: doctree-read (app, doctree) Emitted when a doctree has been parsed and read by the environment, and is @@ -264,7 +326,7 @@ registered event handlers. future reference and should be a child of the returned reference node. .. versionadded:: 0.5 - + .. event:: doctree-resolved (app, doctree, docname) Emitted when a doctree has been "resolved" by the environment, that is, all @@ -280,7 +342,7 @@ registered event handlers. completed, that is, the environment and all doctrees are now up-to-date. .. versionadded:: 0.5 - + .. event:: page-context (app, pagename, templatename, context, doctree) Emitted when the HTML builder has created a context dictionary to render a @@ -311,7 +373,7 @@ registered event handlers. cleanup actions depending on the exception status. .. versionadded:: 0.5 - + .. _template-bridge: diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 3e5ce3e1..ff8d189d 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -68,7 +68,7 @@ directive. Boil the noodle *time* minutes. **Options and advanced usage** - + * If you want to automatically document members, there's a ``members`` option:: @@ -115,9 +115,11 @@ directive. .. versionadded:: 0.4 - * The :dir:`autoclass` and :dir:`autoexception` directives also support a - flag option called ``show-inheritance``. When given, a list of base - classes will be inserted just below the class signature. + * The :dir:`automodule`, :dir:`autoclass` and :dir:`autoexception` directives + also support a flag option called ``show-inheritance``. When given, a list + of base classes will be inserted just below the class signature (when used + with :dir:`automodule`, this will be inserted for every class that is + documented in the module). .. versionadded:: 0.4 @@ -133,6 +135,18 @@ directive. .. versionadded:: 0.5 + * :dir:`automodule` and :dir:`autoclass` also has an ``member-order`` option + that can be used to override the global value of + :confval:`autodoc_member_order` for one directive. + + .. versionadded:: 0.6 + + * The directives supporting member documentation also have a + ``exclude-members`` option that can be used to exclude single member names + from documentation, if all members are to be documented. + + .. versionadded:: 0.6 + .. note:: In an :dir:`automodule` directive with the ``members`` option set, only @@ -142,12 +156,30 @@ directive. .. directive:: autofunction + autodata automethod autoattribute These work exactly like :dir:`autoclass` etc., but do not offer the options used for automatic member documentation. + For module data members and class attributes, documentation can either be put + into a special-formatted comment *before* the attribute definition, or in a + docstring *after* the definition. This means that in the following class + definition, both attributes can be autodocumented:: + + class Foo: + """Docstring for class Foo.""" + + #: Doc comment for attribute Foo.bar. + bar = 1 + + baz = 2 + """Docstring for attribute Foo.baz.""" + + .. versionchanged:: 0.6 + :dir:`autodata` and :dir:`autoattribute` can now extract docstrings. + .. note:: If you document decorated functions or methods, keep in mind that autodoc @@ -162,19 +194,6 @@ directive. There are also new config values that you can set: -.. confval:: automodule_skip_lines - - This value (whose default is ``0``) can be used to skip an amount of lines in - every module docstring that is processed by an :dir:`automodule` directive. - This is provided because some projects like to put headings in the module - docstring, which would then interfere with your sectioning, or automatic - fields with version control tags, that you don't want to put in the generated - documentation. - - .. deprecated:: 0.4 - Use the more versatile docstring processing provided by - :event:`autodoc-process-docstring`. - .. confval:: autoclass_content This value selects what content will be inserted into the main body of an @@ -192,6 +211,14 @@ There are also new config values that you can set: .. versionadded:: 0.3 +.. confval:: autodoc_member_order + + This value selects if automatically documented members are sorted + alphabetical (value ``'alphabetical'``) or by member type (value + ``'groupwise'``). The default is alphabetical. + + .. versionadded:: 0.6 + Docstring preprocessing ----------------------- diff --git a/doc/ext/autosummary.rst b/doc/ext/autosummary.rst new file mode 100644 index 00000000..a9255857 --- /dev/null +++ b/doc/ext/autosummary.rst @@ -0,0 +1,9 @@ +.. highlight:: rest + +:mod:`sphinx.ext.autosummary` -- Generate autodoc summaries +=========================================================== + +.. module:: sphinx.ext.autosummary + :synopsis: Generate autodoc summaries + +TBW. diff --git a/doc/ext/builderapi.rst b/doc/ext/builderapi.rst index adc41016..bb11bfe2 100644 --- a/doc/ext/builderapi.rst +++ b/doc/ext/builderapi.rst @@ -5,7 +5,7 @@ Writing new builders .. todo:: Expand this. -.. currentmodule:: sphinx.builder +.. currentmodule:: sphinx.builders .. class:: Builder @@ -20,7 +20,7 @@ Writing new builders .. automethod:: build_update .. automethod:: build - These methods must be overridden in concrete builder classes: + These methods can be overridden in concrete builder classes: .. automethod:: init .. automethod:: get_outdated_docs @@ -28,3 +28,4 @@ Writing new builders .. automethod:: prepare_writing .. automethod:: write_doc .. automethod:: finish + diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst index 9de6ba9e..19905dc7 100644 --- a/doc/ext/doctest.rst +++ b/doc/ext/doctest.rst @@ -131,7 +131,7 @@ completely equivalent. :: Test-Output example: - .. testcode:: + .. testcode:: parrot.voom(3000) @@ -149,6 +149,14 @@ There are also these config values for customizing the doctest extension: A list of directories that will be added to :data:`sys.path` when the doctest builder is used. (Make sure it contains absolute paths.) +.. confval:: doctest_global_setup + + Python code that is treated like it were put in a ``testsetup`` directive for + *every* file that is tested, and for every group. You can use this to + e.g. import modules you will always need in your doctests. + + .. versionadded:: 0.6 + .. confval:: doctest_test_doctest_blocks If this is a nonempty string (the default is ``'default'``), standard reST @@ -179,7 +187,7 @@ There are also these config values for customizing the doctest extension: >>> print 1 1 - Some more documentation text. + Some more documentation text. This feature makes it easy for you to test doctests in docstrings included with the :mod:`~sphinx.ext.autodoc` extension without marking them up with a diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst new file mode 100644 index 00000000..d007bf25 --- /dev/null +++ b/doc/ext/graphviz.rst @@ -0,0 +1,77 @@ +.. highlight:: rest + +:mod:`sphinx.ext.graphviz` -- Add Graphviz graphs +================================================= + +.. module:: sphinx.ext.graphviz + :synopsis: Support for Graphviz graphs. + +.. versionadded:: 0.6 + +This extension allows you to embed `Graphviz <http://graphviz.org/>`_ graphs in +your documents. + +It adds these directives: + + +.. directive:: graphviz + + Directive to embed graphviz code. The input code for ``dot`` is given as the + content. For example:: + + .. graphviz:: + + digraph foo { + "bar" -> "baz"; + } + + In HTML output, the code will be rendered to a PNG image. In LaTeX output, + the code will be rendered to an embeddable PDF file. + + +.. directive:: graph + + Directive for embedding a single undirected graph. The name is given as a + directive argument, the contents of the graph are the directive content. + This is a convenience directive to generate ``graph <name> { <content> }``. + + For example:: + + .. graph:: foo + + "bar" -- "baz"; + + +.. directive:: digraph + + Directive for embedding a single directed graph. The name is given as a + directive argument, the contents of the graph are the directive content. + This is a convenience directive to generate ``digraph <name> { <content> }``. + + For example:: + + .. digraph:: foo + + "bar" -> "baz" -> "quux"; + + +There are also these new config values: + +.. confval:: graphviz_dot + + The command name with which to invoke ``dot``. The default is ``'dot'``; you + may need to set this to a full path if ``dot`` is not in the executable + search path. + + Since this setting is not portable from system to system, it is normally not + useful to set it in ``conf.py``; rather, giving it on the + :program:`sphinx-build` command line via the :option:`-D` option should be + preferable, like this:: + + sphinx-build -b html -D graphviz_dot=C:\graphviz\bin\dot.exe . _build/html + +.. confval:: graphviz_dot_args + + Additional command-line arguments to give to dot, as a list. The default is + an empty list. This is the right place to set global graph, node or edge + attributes via dot's ``-G``, ``-N`` and ``-E`` options. diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst new file mode 100644 index 00000000..fe6d636d --- /dev/null +++ b/doc/ext/inheritance.rst @@ -0,0 +1,46 @@ +.. highlight:: rest + +:mod:`sphinx.ext.inheritance_diagram` -- Include inheritance diagrams +===================================================================== + +.. module:: sphinx.ext.inheritance_diagram + :synopsis: Support for displaying inheritance diagrams via graphviz. + +.. versionadded:: 0.6 + +This extension allows you to include inheritance diagrams, rendered via the +:mod:`Graphviz extension <sphinx.ext.graphviz>`. + +It adds this directive: + +.. directive:: inheritance-diagram + + This directive has one or more arguments, each giving a module or class + name. Class names can be unqualified; in that case they are taken to exist + in the currently described module (see :dir:`module`). + + For each given class, and each class in each given module, the base classes + are determined. Then, from all classes and their base classes, a graph is + generated which is then rendered via the graphviz extension to a directed + graph. + + This directive supports an option called ``parts`` that, if given, must be an + integer, advising the directive to remove that many parts of module names + from the displayed names. (For example, if all your class names start with + ``lib.``, you can give ``:parts: 1`` to remove that prefix from the displayed + node names.) + + +New config values are: + +.. confval:: inheritance_graph_attrs + + A dictionary of graphviz graph attributes for inheritance diagrams. + +.. confval:: inheritance_node_attrs + + A dictionary of graphviz node attributes for inheritance diagrams. + +.. confval:: inheritance_edge_attrs + + A dictionary of graphviz edge attributes for inheritance diagrams. diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst index befae2c0..302ab6a3 100644 --- a/doc/ext/intersphinx.rst +++ b/doc/ext/intersphinx.rst @@ -49,7 +49,7 @@ linking: This will download the corresponding :file:`objects.inv` file from the Internet and generate links to the pages under the given URI. The downloaded inventory is cached in the Sphinx environment, so it must be redownloaded - whenever you do a full rebuild. + whenever you do a full rebuild. A second example, showing the meaning of a non-``None`` value:: diff --git a/doc/ext/math.rst b/doc/ext/math.rst index f2664dfe..a214b41e 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -17,8 +17,9 @@ if possible, reuse that support too. .. note:: - :mod:`sphinx.ext.mathbase` does not need to be added to the - :confval:`extensions` config value. + :mod:`sphinx.ext.mathbase` is not meant to be added to the + :confval:`extensions` config value, instead, use either + :mod:`sphinx.ext.pngmath` or :mod:`sphinx.ext.jsmath` as described below. The input language for mathematics is LaTeX markup. This is the de-facto standard for plain-text math notation and has the added advantage that no @@ -90,7 +91,7 @@ further translation is necessary when building LaTeX output. Euler's identity, equation :eq:`euler`, was elected one of the most beautiful mathematical formulas. - + :mod:`sphinx.ext.pngmath` -- Render math as PNG images ------------------------------------------------------ @@ -107,8 +108,8 @@ There are various config values you can set to influence how the images are buil .. confval:: pngmath_latex The command name with which to invoke LaTeX. The default is ``'latex'``; you - may need to set this to a full path if ``latex`` not in the executable search - path. + may need to set this to a full path if ``latex`` is not in the executable + search path. Since this setting is not portable from system to system, it is normally not useful to set it in ``conf.py``; rather, giving it on the @@ -133,7 +134,7 @@ There are various config values you can set to influence how the images are buil list. .. versionadded:: 0.5.1 - + .. confval:: pngmath_latex_preamble Additional LaTeX code to put into the preamble of the short LaTeX files that @@ -145,7 +146,7 @@ There are various config values you can set to influence how the images are buil Additional arguments to give to dvipng, as a list. The default value is ``['-gamma 1.5', '-D 110']`` which makes the image a bit darker and larger then it is by default. - + An arguments you might want to add here is e.g. ``'-bg Transparent'``, which produces PNGs with a transparent background. This is not enabled by default because some Internet Explorer versions don't like transparent PNGs. diff --git a/doc/ext/todo.rst b/doc/ext/todo.rst index 7bc65a02..4f5a379d 100644 --- a/doc/ext/todo.rst +++ b/doc/ext/todo.rst @@ -21,9 +21,9 @@ There are two additional directives when using this extension: This directive is replaced by a list of all todo directives in the whole documentation, if :confval:`todo_include_todos` is true. - + There is also an additional config value: - + .. confval:: todo_include_todos If this is ``True``, :dir:`todo` and :dir:`todolist` produce output, else diff --git a/doc/ext/tutorial.rst b/doc/ext/tutorial.rst index ae9b7a77..c44748d2 100644 --- a/doc/ext/tutorial.rst +++ b/doc/ext/tutorial.rst @@ -109,8 +109,8 @@ new Python module called :file:`todo.py` and add the setup function:: latex=(visit_todo_node, depart_todo_node), text=(visit_todo_node, depart_todo_node)) - app.add_directive('todo', todo_directive, 1, (0, 0, 1)) - app.add_directive('todolist', todolist_directive, 0, (0, 0, 0)) + app.add_directive('todo', TodoDirective) + app.add_directive('todolist', TodolistDirective) app.connect('doctree-resolved', process_todo_nodes) app.connect('env-purge-doc', purge_todos) @@ -132,9 +132,7 @@ the individual calls do is the following: We need to create the two node classes ``todo`` and ``todolist`` later. -* :meth:`~Sphinx.add_directive` adds a new *directive*, given by name, handler - function and two arguments that specify if the directive has content and how - many arguments it accepts. +* :meth:`~Sphinx.add_directive` adds a new *directive*, given by name and class. The handler functions are created later. @@ -158,7 +156,7 @@ Let's start with the node classes:: def visit_todo_node(self, node): self.visit_admonition(node) - + def depart_todo_node(self, node): self.depart_admonition(node) @@ -168,17 +166,25 @@ docutils classes defined in :mod:`docutils.nodes`. ``todo`` inherits from is just a "general" node. -The Directive Handlers ----------------------- +The Directive Classes +--------------------- -A directive handler is a function with a host of arguments, covered in detail in -the docutils documentation. It must return a list of nodes. +A directive class is a class deriving usually from +``docutils.parsers.rst.Directive``. Since the class-based directive interface +doesn't exist yet in Docutils 0.4, Sphinx has another base class called +``sphinx.util.compat.Directive`` that you can derive your directive from, and it +will work with both Docutils 0.4 and 0.5 upwards. The directive interface is +covered in detail in the docutils documentation; the important thing is that the +class has a method ``run`` that returns a list of nodes. The ``todolist`` directive is quite simple:: - def todolist_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return [todolist('')] + from sphinx.util.compat import Directive + + class TodolistDirective(Directive): + + def run(self): + return [todolist('')] An instance of our ``todolist`` node class is created and returned. The todolist directive has neither content nor arguments that need to be handled. @@ -187,30 +193,35 @@ The ``todo`` directive function looks like this:: from sphinx.util.compat import make_admonition - def todo_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - - targetid = "todo-%s" % env.index_num - env.index_num += 1 - targetnode = nodes.target('', '', ids=[targetid]) - - ad = make_admonition(todo, name, [_('Todo')], options, content, lineno, - content_offset, block_text, state, state_machine) - - if not hasattr(env, 'todo_all_todos'): - env.todo_all_todos = [] - env.todo_all_todos.append({ - 'docname': env.docname, - 'lineno': lineno, - 'todo': ad[0].deepcopy(), - 'target': targetnode, - }) - - return [targetnode] + ad + class TodoDirective(Directive): + + # this enables content in the directive + has_content = True + + def run(self): + env = self.state.document.settings.env + + targetid = "todo-%s" % env.index_num + env.index_num += 1 + targetnode = nodes.target('', '', ids=[targetid]) + + ad = make_admonition(todo, self.name, [_('Todo')], self.options, + self.content, self.lineno, self.content_offset, + self.block_text, self.state, self.state_machine) + + if not hasattr(env, 'todo_all_todos'): + env.todo_all_todos = [] + env.todo_all_todos.append({ + 'docname': env.docname, + 'lineno': self.lineno, + 'todo': ad[0].deepcopy(), + 'target': targetnode, + }) + + return [targetnode] + ad Several important things are covered here. First, as you can see, you can refer -to the build environment instance using ``state.document.settings.env``. +to the build environment instance using ``self.state.document.settings.env``. Then, to act as a link target (from the todolist), the todo directive needs to return a target node in addition to the todo node. The target ID (in HTML, this @@ -236,6 +247,22 @@ node. In the last line, the nodes that should be put into the doctree are returned: the target node and the admonition node. +The node structure that the directive returns looks like this:: + + +--------------------+ + | target node | + +--------------------+ + +--------------------+ + | todo node | + +--------------------+ + \__+--------------------+ + | admonition title | + +--------------------+ + | paragraph | + +--------------------+ + | ... | + +--------------------+ + The Event Handlers ------------------ @@ -262,20 +289,20 @@ emitted at the end of phase 3 and allows custom resolving to be done:: def process_todo_nodes(app, doctree, fromdocname): if not app.config.todo_include_todos: - for node in doctree.traverse(todo_node): + for node in doctree.traverse(todo): node.parent.remove(node) - + # Replace all todolist nodes with a list of the collected todos. # Augment each todo with a backlink to the original location. env = app.builder.env - + for node in doctree.traverse(todolist): if not app.config.todo_include_todos: node.replace_self([]) continue - + content = [] - + for todo_info in env.todo_all_todos: para = nodes.paragraph() filename = env.doc2path(todo_info['docname'], base=None) @@ -283,7 +310,7 @@ emitted at the end of phase 3 and allows custom resolving to be done:: _('(The original entry is located in %s, line %d and can be found ') % (filename, todo_info['lineno'])) para += nodes.Text(description, description) - + # Create a reference newnode = nodes.reference('', '') innernode = nodes.emphasis(_('here'), _('here')) @@ -294,11 +321,11 @@ emitted at the end of phase 3 and allows custom resolving to be done:: newnode.append(innernode) para += newnode para += nodes.Text('.)', '.)') - + # Insert into the todolist content.append(todo_info['todo']) content.append(para) - + node.replace_self(content) It is a bit more involved. If our new "todo_include_todos" config value is diff --git a/doc/extensions.rst b/doc/extensions.rst index 12c82da5..5eb26c14 100644 --- a/doc/extensions.rst +++ b/doc/extensions.rst @@ -41,9 +41,12 @@ These extensions are built in and can be activated by respective entries in the .. toctree:: ext/autodoc + ext/autosummary ext/doctest ext/intersphinx ext/math + ext/graphviz + ext/inheritance ext/refcounting ext/ifconfig ext/coverage diff --git a/doc/faq.rst b/doc/faq.rst new file mode 100644 index 00000000..723601f1 --- /dev/null +++ b/doc/faq.rst @@ -0,0 +1,34 @@ +.. _faq: + +Sphinx FAQ +========== + +This is a list of Frequently Asked Questions about Sphinx. Feel free to +suggest new entries! + +How do I... +----------- + +... get section numbers? + They are automatic in LaTeX output; for HTML, give a ``:numbered:`` option to + the :dir:`toctree` directive where you want to start numbering. + +... customize the look of the built HTML files? + Use themes, see :doc:`theming`. + +... add global substitutions or includes? + Add them in the :confval:`rst_epilog` config value. + +... write my own extension? + See the :ref:`extension tutorial <exttut>`. + +... use Sphinx with Epydoc? + There's a third-party extension providing an `api role`_ which refers to + Epydoc's API docs for a given identifier. + +... use Sphinx with SCons? + Glenn Hutchings has written a SCons build script to build Sphinx + documentation; it is hosted here: http://bitbucket.org/zondo/sphinx-scons + + +.. _api role: http://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py diff --git a/doc/glossary.rst b/doc/glossary.rst index 6a80ad36..7ec787ff 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -6,7 +6,7 @@ Glossary .. glossary:: builder - A class (inheriting from :class:`~sphinx.builder.Builder`) that takes + A class (inheriting from :class:`~sphinx.builders.Builder`) that takes parsed documents and performs an action on them. Normally, builders translate the documents to an output format, but it is also possible to use the builder builders that e.g. check for broken links in the diff --git a/doc/intro.rst b/doc/intro.rst index 47e016b3..7b8f8651 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -89,6 +89,12 @@ The :program:`sphinx-build` script has several more options: cross-references), but rebuild it completely. The default is to only read and parse source files that are new or have changed since the last run. +**-t** *tag* + Define the tag *tag*. This is relevant for :dir:`only` directives that only + include their content if this tag is set. + + .. versionadded:: 0.6 + **-d** *path* Since Sphinx has to read and parse all source files before it can write an output file, the parsed source files are cached as "doctree pickles". @@ -109,10 +115,14 @@ The :program:`sphinx-build` script has several more options: Don't look for a configuration file; only take options via the ``-D`` option. .. versionadded:: 0.5 - + **-D** *setting=value* - Override a configuration value set in the :file:`conf.py` file. (The value - must be a string value.) + Override a configuration value set in the :file:`conf.py` file. The value + must be a string or dictionary value. For the latter, supply the setting + name and key like this: ``-D latex_elements.docclass=scrartcl``. + + .. versionchanged:: 0.6 + The value can now be a dictionary value. **-A** *name=value* Make the *name* assigned to *value* in the HTML templates. @@ -129,6 +139,13 @@ The :program:`sphinx-build` script has several more options: Do not output anything on standard output, also suppress warnings. Only errors are written to standard error. +**-w** *file* + Write warnings (and errors) to the given file, in addition to standard error. + +**-W** + Turn warnings into errors. This means that the build stops at the first + warning and ``sphinx-build`` exits with exit status 1. + **-P** (Useful for debugging only.) Run the Python debugger, :mod:`pdb`, if an unhandled exception occurs while building. diff --git a/doc/markup/code.rst b/doc/markup/code.rst index 6b2fbd02..93cd127b 100644 --- a/doc/markup/code.rst +++ b/doc/markup/code.rst @@ -99,12 +99,14 @@ Includes .. literalinclude:: example.py - The file name is relative to the current file's path. + The file name is usually relative to the current file's path. However, if it + is absolute (starting with ``/``), it is relative to the top source + directory. The directive also supports the ``linenos`` flag option to switch on line numbers, and a ``language`` option to select a language different from the current file's standard language. Example with options:: - + .. literalinclude:: example.rb :language: ruby :linenos: @@ -116,8 +118,36 @@ Includes .. literalinclude:: example.py :encoding: latin-1 + The directive also supports including only parts of the file. If it is a + Python module, you can select a class, function or method to include using + the ``pyobject`` option:: + + .. literalinclude:: example.py + :pyobject: Timer.start + + This would only include the code lines belonging to the ``start()`` method in + the ``Timer`` class within the file. + + Alternately, you can specify exactly which lines to include by giving a + ``lines`` option:: + + .. literalinclude:: example.py + :lines: 1,3,5-10,20- + + This includes the lines 1, 3, 5 to 10 and lines 20 to the last line. + + Another way to control which part of the file is included is to use the + ``start-after`` and ``end-before`` options (or only one of them). If + ``start-after`` is given as a string option, only lines that follow the first + line containing that string are included. If ``end-before`` is given as a + string option, only lines that precede the first lines containing that string + are included. + .. versionadded:: 0.4.3 The ``encoding`` option. + .. versionadded:: 0.6 + The ``pyobject``, ``lines``, ``start-after`` and ``end-before`` options, + as well as support for absolute filenames. .. rubric:: Footnotes diff --git a/doc/markup/desc.rst b/doc/markup/desc.rst index 892cdc4a..ec8ede37 100644 --- a/doc/markup/desc.rst +++ b/doc/markup/desc.rst @@ -205,6 +205,12 @@ The directives are: .. versionadded:: 0.4 +.. directive:: .. classmethod:: name(signature) + + Like :dir:`method`, but indicates that the method is a class method. + + .. versionadded:: 0.6 + .. _signatures: @@ -254,7 +260,7 @@ explained by an example:: .. function:: format_exception(etype, value, tb[, limit=None]) Format the exception with a traceback. - + :param etype: exception type :param value: exception value :param tb: traceback object diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index 5dbd638e..69721b32 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -224,7 +224,53 @@ to labels: Using :role:`ref` is advised over standard reStructuredText links to sections (like ```Section title`_``) because it works across files, when section headings are changed, and for all builders that support cross-references. - + + +Cross-referencing documents +--------------------------- + +.. versionadded:: 0.6 + +There is also a way to directly link to documents: + +.. role:: doc + + Link to the specified document; the document name can be specified in + absolute or relative fashion. For example, if the reference + ``:doc:`parrot``` occurs in the document ``sketches/index``, then the link + refers to ``sketches/parrot``. If the reference is ``:doc:`/people``` or + ``:doc:`../people```, the link refers to ``people``. + + If no explicit link text is given (like usual: ``:doc:`Monty Python members + </people>```), the link caption will be the title of the given document. + + +Referencing downloadable files +------------------------------ + +.. versionadded:: 0.6 + +.. role:: download + + This role lets you link to files within your source tree that are not reST + documents that can be viewed, but files that can be downloaded. + + When you use this role, the referenced file is automatically marked for + inclusion in the output when building (obviously, for HTML output only). + All downloadable files are put into the ``_downloads`` subdirectory of the + output directory; duplicate filenames are handled. + + An example:: + + See :download:`this example script <../example.py>`. + + The given filename is usually relative to the directory the current source + file is contained in, but if it absolute (starting with ``/``), it is taken + as relative to the top source directory. + + The ``example.py`` file will be copied to the output directory, and a + suitable link generated to it. + Other semantic markup --------------------- @@ -232,6 +278,16 @@ Other semantic markup The following roles don't do anything special except formatting the text in a different style: +.. role:: abbr + + An abbreviation. If the role content contains a parenthesized explanation, + it will be treated specially: it will be shown in a tool-tip in HTML, and + output only once in LaTeX. + + Example: ``:abbr:`LIFO (last-in, first-out)```. + + .. versionadded:: 0.6 + .. role:: command The name of an OS-level command, such as ``rm``. @@ -330,7 +386,7 @@ in a different style: curly braces to indicate a "variable" part, as in ``:file:``. If you don't need the "variable part" indication, use the standard - ````code```` instead. + ````code```` instead. The following roles generate external links: @@ -351,6 +407,7 @@ The following roles generate external links: Note that there are no special roles for including hyperlinks as you can use the standard reST markup for that purpose. + .. _default-substitutions: Substitutions diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst index 98f5485e..01e5a3f1 100644 --- a/doc/markup/misc.rst +++ b/doc/markup/misc.rst @@ -3,6 +3,8 @@ Miscellaneous markup ==================== +.. _metadata: + File-wide metadata ------------------ @@ -46,6 +48,28 @@ Meta-information markup output. +.. _tags: + +Including content based on tags +------------------------------- + +.. directive:: .. only:: <expression> + + Include the content of the directive only if the *expression* is true. The + expression should consist of tags, like this:: + + .. only:: html and draft + + Undefined tags are false, defined tags (via the ``-t`` command-line option or + within :file:`conf.py`) are true. Boolean expressions, also using + parentheses (like ``html and (latex or draft)`` are supported. + + The format of the current builder (``html``, ``latex`` or ``text``) is always + set as a tag. + + .. versionadded:: 0.6 + + Tables ------ diff --git a/doc/markup/para.rst b/doc/markup/para.rst index c60eb258..e8adc75c 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -1,4 +1,4 @@ -.. highlight:: rest +x.. highlight:: rest Paragraph-level markup ---------------------- @@ -85,9 +85,9 @@ units as well as normal text: This directive creates a paragraph heading that is not used to create a table of contents node. - + .. note:: - + If the *title* of the rubric is "Footnotes", this rubric is ignored by the LaTeX writer, since it is assumed to only contain footnote definitions and therefore would create an empty heading. @@ -100,6 +100,27 @@ units as well as normal text: .. centered:: LICENSE AGREEMENT +.. directive:: hlist + + This directive must contain a bullet list. It will transform it into a more + compact list by either distributing more than one item horizontally, or + reducing spacing between items, depending on the builder. + + For builders that support the horizontal distribution, there is a ``columns`` + option that specifies the number of columns; it defaults to 2. Example:: + + .. hlist:: + :columns: 3 + + * A list of + * short items + * that should be + * displayed + * horizontally + + .. versionadded:: 0.6 + + Table-of-contents markup ------------------------ @@ -182,14 +203,18 @@ Glossary .. glossary:: environment - A structure where information about all documents under the root is saved, - and used for cross-referencing. The environment is pickled after the - parsing stage, so that successive runs only need to read and parse new and - changed documents. + A structure where information about all documents under the root is + saved, and used for cross-referencing. The environment is pickled + after the parsing stage, so that successive runs only need to read + and parse new and changed documents. source directory - The directory which, including its subdirectories, contains all source - files for one Sphinx project. + The directory which, including its subdirectories, contains all + source files for one Sphinx project. + + .. versionadded:: 0.6 + You can now give the glossary directive a ``:sorted:`` flag that will + automatically sort the entries alphabetically. Grammar production displays @@ -218,7 +243,7 @@ the definition of the symbol. There is this directive: Note that no further reST parsing is done in the production, so that you don't have to escape ``*`` or ``|`` characters. -.. XXX describe optional first parameter +.. XXX describe optional first parameter The following is an example taken from the Python Reference Manual:: diff --git a/doc/rest.rst b/doc/rest.rst index d7ef5c7b..e70fa105 100644 --- a/doc/rest.rst +++ b/doc/rest.rst @@ -212,10 +212,14 @@ reST supports an image directive, used like so:: .. image:: gnu.png (options) -When used within Sphinx, the file name given (here ``gnu.png``) must be relative -to the source file, and Sphinx will automatically copy image files over to a -subdirectory of the output directory on building (e.g. the ``_static`` directory -for HTML output.) +When used within Sphinx, the file name given (here ``gnu.png``) must either be +relative to the source file, or absolute which means that they are relative to +the top source directory. For example, the file ``sketch/spam.rst`` could refer +to the image ``images/spam.png`` as ``../images/spam.png`` or +``/images/spam.png``. + +Sphinx will automatically copy image files over to a subdirectory of the output +directory on building (e.g. the ``_static`` directory for HTML output.) Interpretation of image size options (``width`` and ``height``) is as follows: if the size has no unit or the unit is pixels, the given size will only be @@ -236,6 +240,9 @@ the former, while the HTML builder would prefer the latter. .. versionchanged:: 0.4 Added the support for file names ending in an asterisk. +.. versionchanged:: 0.6 + Image paths can now be absolute. + Footnotes --------- @@ -282,7 +289,7 @@ markup blocks, like this:: See the `reST reference for substitutions <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#substitution-definitions>`_ for details. - + If you want to use some substitutions for all documents, put them into a separate file and include it into all documents you want to use them in, using the :dir:`include` directive. Be sure to give the include file a file name @@ -301,7 +308,7 @@ footnotes above) is regarded as a comment. For example:: .. This is a comment. You can indent text after a comment start to form multiline comments:: - + .. This whole indented block is a comment. @@ -329,5 +336,5 @@ There are some problems one commonly runs into while authoring reST documents: * **No nested inline markup:** Something like ``*see :func:`foo`*`` is not possible. - + .. XXX more? diff --git a/doc/sphinx-build.1 b/doc/sphinx-build.1 new file mode 100644 index 00000000..498771c9 --- /dev/null +++ b/doc/sphinx-build.1 @@ -0,0 +1,102 @@ +.TH sphinx-build 1 "Jan 2009" "Sphinx 0.6" "User Commands" +.SH NAME +sphinx-build \- Sphinx documentation generator tool +.SH SYNOPSIS +.B sphinx-build +[\fIoptions\fR] <\fIsourcedir\fR> <\fIoutdir\fR> [\fIfilenames\fR...] +.SH DESCRIPTION +sphinx-build generates documentation from the files in <sourcedir> and places it +in the <outdir>. + +sphinx-build looks for <sourcedir>/conf.py for the configuration settings. +.B sphinx-quickstart(1) +may be used to generate template files, including conf.py. + +sphinx-build can create documentation in different formats. A format is +selected by specifying the builder name on the command line; it defaults to +HTML. Builders can also perform other tasks related to documentation +processing. + +By default, everything that is outdated is built. Output only for selected +files can be built by specifying individual filenames. + +List of available builders: +.TP +\fBhtml\fR +HTML files generation. This is default builder. +.TP +\fBhtmlhelp\fR +Generates files for CHM generation. +.TP +\fBqthelp\fR +Generates files for Qt help collection generation. +.TP +\fBlatex\fR +Generates a LaTeX version of the documentation. +.TP +\fBtext\fR +Generates a plain-text version of the documentation. +.TP +\fBchanges\fR +Generates HTML files listing changed/added/deprecated items for the +current version. +.TP +\fBlinkcheck\fR +Checks the integrity of all external links in the documentation. +.TP +\fBpickle / json\fR +Generates serialized HTML files in the selected format. + +.SH OPTIONS +.TP +\fB-b\fR <builder> +Builder to use; defaults to html. See the full list of builders above. +.TP +\fB-a\fR +Generates output for all files; without this option only output for +new and changed files is generated. +.TP +\fB-E\fR +Ignores cached files, forces to re-read all source files from disk. +.TP +\fB-c\fR <path> +Locates the conf.py file in the specified path instead of <sourcedir>. +.TP +\fB-C\fR +Specifies that no conf.py file at all is to be used. Configuration can +only be set with the -D option. +.TP +\fB-D\fR <setting>=<value> +Overrides a setting from the configuration file. +.TP +\fB-d\fR <path> +Path to cached files; defaults to <outdir>/.doctrees. +.TP +\fB-A\fR <name>=<value> +Passes a value into the HTML templates (only for html builders). +.TP +\fB-N\fR +Prevents colored output. +.TP +\fB-q\fR +Quiet operation, just prints warnings and errors on stderr. +.TP +\fB-Q\fR +Very quiet operation, doesn't print anything except for errors. +.TP +\fB-w\fR <file> +Write warnings and errors into the given file, in addition to stderr. +.TP +\fB-W\fR +Turn warnings into errors. +.TP +\fB-P\fR +Runs Pdb on exception. +.SH "SEE ALSO" +.BR sphinx-quickstart(1) +.SH AUTHOR +Georg Brandl <georg@python.org>, Armin Ronacher <armin.ronacher@active-4.com> et +al. +.PP +This manual page was initially written by Mikhail Gusarov +<dottedmag@dottedmag.net>, for the Debian project. diff --git a/doc/sphinx-quickstart.1 b/doc/sphinx-quickstart.1 new file mode 100644 index 00000000..93b0a4a5 --- /dev/null +++ b/doc/sphinx-quickstart.1 @@ -0,0 +1,17 @@ +.TH sphinx-quickstart 1 "Jan 2009" "Sphinx 0.6" "User Commands" +.SH NAME +sphinx-quickstart \- Sphinx documentation template generator +.SH SYNOPSIS +.B sphinx-quickstart +.SH DESCRIPTION +sphinx-quickstart is an interactive tool that asks some questions about your +project and then generates a complete documentation directory and sample +Makefile to be used with \fBsphinx-build(1)\fR. +.SH "SEE ALSO" +.BR sphinx-build(1) +.SH AUTHOR +Georg Brandl <georg@python.org>, Armin Ronacher <armin.ronacher@active-4.com> et +al. +.PP +This manual page was initially written by Mikhail Gusarov +<dottedmag@dottedmag.net> for the Debian project. diff --git a/doc/templating.rst b/doc/templating.rst index 61a8a72b..61657547 100644 --- a/doc/templating.rst +++ b/doc/templating.rst @@ -1,3 +1,5 @@ +.. highlight:: html+jinja + .. _templating: Templating @@ -19,10 +21,10 @@ No. You have several other options: configuration value accordingly. * You can :ref:`write a custom builder <writing-builders>` that derives from - :class:`~sphinx.builder.StandaloneHTMLBuilder` and calls your template engine + :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine of choice. -* You can use the :class:`~sphinx.builder.PickleHTMLBuilder` that produces +* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces pickle files with the page contents, and postprocess them using a custom tool, or use them in your Web application. @@ -37,27 +39,26 @@ template, customizing it while also keeping the changes at a minimum. To customize the output of your documentation you can override all the templates (both the layout templates and the child templates) by adding files with the -same name as the original filename into the template directory of the folder the -Sphinx quickstart generated for you. +same name as the original filename into the template directory of the structure +the Sphinx quickstart generated for you. Sphinx will look for templates in the folders of :confval:`templates_path` first, and if it can't find the template it's looking for there, it falls back -to the builtin templates that come with Sphinx. +to the selected theme's templates. A template contains **variables**, which are replaced with values when the template is evaluated, **tags**, which control the logic of the template and **blocks** which are used for template inheritance. -Sphinx provides base templates with a couple of blocks it will fill with data. -The default templates are located in the :file:`templates` folder of the Sphinx -installation directory. Templates with the same name in the -:confval:`templates_path` override templates located in the builtin folder. +Sphinx' *basic* theme provides base templates with a couple of blocks it will +fill with data. These are located in the :file:`themes/basic` subdirectory of +the Sphinx installation directory, and used by all builtin Sphinx themes. +Templates with the same name in the :confval:`templates_path` override templates +supplied by the selected theme. For example, to add a new link to the template area containing related links all you have to do is to add a new template called ``layout.html`` with the -following contents: - -.. sourcecode:: html+jinja +following contents:: {% extends "!layout.html" %} {% block rootrellink %} @@ -65,16 +66,24 @@ following contents: {{ super() }} {% endblock %} -By prefixing the name of the extended template with an exclamation mark, Sphinx -will load the builtin layout template. If you override a block, you should call -``{{ super() }}`` somewhere to render the block's content in the extended -template -- unless you don't want that content to show up. +By prefixing the name of the overridden template with an exclamation mark, +Sphinx will load the layout template from the underlying HTML theme. + +**Important**: If you override a block, call ``{{ super() }}`` somewhere to +render the block's content in the extended template -- unless you don't want +that content to show up. + + +Working the the builtin templates +--------------------------------- +The builtin **basic** theme supplies the templates that all builtin Sphinx +themes are based on. It has the following elements you can override or use: Blocks ~~~~~~ -The following blocks exist in the ``layout`` template: +The following blocks exist in the ``layout.html`` template: `doctype` The doctype of the output format. By default this is XHTML 1.0 Transitional @@ -92,11 +101,11 @@ The following blocks exist in the ``layout`` template: add references to JavaScript or extra CSS files. `relbar1` / `relbar2` - This block contains the list of related links (the parent documents on the - left, and the links to index, modules etc. on the right). `relbar1` appears - before the document, `relbar2` after the document. By default, both blocks - are filled; to show the relbar only before the document, you would override - `relbar2` like this:: + This block contains the *relation bar*, the list of related links (the + parent documents on the left, and the links to index, modules etc. on the + right). `relbar1` appears before the document, `relbar2` after the + document. By default, both blocks are filled; to show the relbar only + before the document, you would override `relbar2` like this:: {% block relbar2 %}{% endblock %} @@ -109,7 +118,8 @@ The following blocks exist in the ``layout`` template: the :data:`reldelim1`. `document` - The contents of the document itself. + The contents of the document itself. It contains the block "body" where the + individual content is put by subtemplates like ``page.html``. `sidebar1` / `sidebar2` A possible location for a sidebar. `sidebar1` appears before the document @@ -135,6 +145,10 @@ The following blocks exist in the ``layout`` template: `sidebarrel` The relation links (previous, next document) within the sidebar. +`sidebarsourcelink` + The "Show source" link within the sidebar (normally only shown if this is + enabled by :confval:`html_show_sourcelink`). + `sidebarsearch` The search box within the sidebar. Override this if you want to place some content at the bottom of the sidebar. @@ -162,13 +176,17 @@ using the ``{% set %}`` tag: defaults to ``' |'``. Each item except of the last one in the related bar ends with the value of this variable. -Overriding works like this: - -.. sourcecode:: html+jinja +Overriding works like this:: {% extends "!layout.html" %} {% set reldelim1 = ' >' %} +.. data:: script_files + + Add additional script files here, like this:: + + {% set script_files = script_files + [pathto("_static/myscript.js", 1)] %} + Helper Functions ~~~~~~~~~~~~~~~~ @@ -196,7 +214,7 @@ them to generate links or output multiply used elements. .. function:: relbar() - Return the rendered relbar. + Return the rendered relation bar. Global Variables @@ -206,32 +224,145 @@ These global variables are available in every template and are safe to use. There are more, but most of them are an implementation detail and might change in the future. +.. data:: builder + + The name of the builder (e.g. ``html`` or ``htmlhelp``). + +.. data:: copyright + + The value of :confval:`copyright`. + .. data:: docstitle The title of the documentation (the value of :confval:`html_title`). -.. data:: sourcename +.. data:: embedded - The name of the copied source file for the current document. This is only - nonempty if the :confval:`html_copy_source` value is true. + True if the built HTML is meant to be embedded in some viewing application + that handles navigation, not the web browser, such as for HTML help or Qt + help formats. In this case, the sidebar is not included. -.. data:: builder +.. data:: favicon + + The path to the HTML favicon in the static path, or ``''``. + +.. data:: file_suffix + + The value of the builder's :attr:`out_suffix` attribute, i.e. the file name + extension that the output files will get. For a standard HTML builder, this + is usually ``.html``. + +.. data:: has_source + + True if the reST document sources are copied (if :confval:`html_copy_source` + is true). + +.. data:: last_updated + + The build date. - The name of the builder (for builtin builders, ``html``, ``htmlhelp``, or - ``web``). +.. data:: logo + + The path to the HTML logo image in the static path, or ``''``. + +.. data:: master_doc + + The value of :confval:`master_doc`, for usage with :func:`pathto`. .. data:: next The next document for the navigation. This variable is either false or has two attributes `link` and `title`. The title contains HTML markup. For - example, to generate a link to the next page, you can use this snippet: - - .. sourcecode:: html+jinja + example, to generate a link to the next page, you can use this snippet:: {% if next %} <a href="{{ next.link|e }}">{{ next.title }}</a> {% endif %} +.. data:: pagename + + The "page name" of the current file, i.e. either the document name if the + file is generated from a reST source, or the equivalent hierarchical name + relative to the output directory (``[directory/]filename_without_extension``). + +.. data:: parents + + A list of parent documents for navigation, structured like the :data:`next` + item. + .. data:: prev Like :data:`next`, but for the previous page. + +.. data:: project + + The value of :confval:`project`. + +.. data:: release + + The value of :confval:`release`. + +.. data:: rellinks + + A list of links to put at the left side of the relbar, next to "next" and + "prev". This usually contains links to the index and the modindex. If you + add something yourself, it must be a tuple ``(pagename, link title, + accesskey, link text)``. + +.. data:: shorttitle + + The value of :confval:`html_short_title`. + +.. data:: show_source + + True if :confval:`html_show_sourcelink` is true. + +.. data:: sphinx_version + + The version of Sphinx used to build. + +.. data:: style + + The name of the main stylesheet, as given by the theme or + :confval:`html_style`. + +.. data:: title + + The title of the current document, as used in the ``<title>`` tag. + +.. data:: use_opensearch + + The value of :confval:`html_use_opensearch`. + +.. data:: version + + The value of :confval:`version`. + + +In addition to these values, there are also all **theme options** available +(prefixed by ``theme_``), as well as the values given by the user in +:confval:`html_context`. + +In documents that are created from source files (as opposed to +automatically-generated files like the module index, or documents that already +are in HTML form), these variables are also available: + +.. data:: meta + + Document metadata, see :ref:`metadata`. + +.. data:: sourcename + + The name of the copied source file for the current document. This is only + nonempty if the :confval:`html_copy_source` value is true. + +.. data:: toc + + The local table of contents for the current page, rendered as HTML bullet + lists. + +.. data:: toctree + + A callable yielding the global TOC tree containing the current page, rendered + as HTML bullet lists. If the optional keyword argument ``collapse`` is true, + all TOC entries that are not ancestors of the current page are collapsed. diff --git a/doc/theming.rst b/doc/theming.rst new file mode 100644 index 00000000..3e3a33c8 --- /dev/null +++ b/doc/theming.rst @@ -0,0 +1,187 @@ +.. highlightlang:: python + +HTML theming support +==================== + +.. versionadded:: 0.6 + +Sphinx supports changing the appearance of its HTML output via *themes*. A +theme is a collection of HTML templates, stylesheet(s) and other static files. +Additionally, it has a configuration file which specifies from which theme to +inherit, which highlighting style to use, and what options exist for customizing +the theme's look and feel. + +Themes are meant to be project-unaware, so they can be used for different +projects without change. + + +Using a theme +------------- + +Using an existing theme is easy. If the theme is builtin to Sphinx, you only +need to set the :confval:`html_theme` config value. With the +:confval:`html_theme_options` config value you can set theme-specific options +that change the look and feel. For example, you could have the following in +your :file:`conf.py`:: + + html_theme = "default" + html_theme_options = { + "rightsidebar": "true", + "relbarbgcolor: "black" + } + +That would give you the default theme, but with a sidebar on the right side and +a black background for the relation bar (the bar with the navigation links at +the page's top and bottom). + +If the theme does not come with Sphinx, it can be in two forms: either a +directory (containing :file:`theme.conf` and other needed files), or a zip file +with the same contents. Either of them must be put where Sphinx can find it; +for this there is the config value :confval:`html_theme_path`. It gives a list +of directories, relative to the directory containing :file:`conf.py`, that can +contain theme directories or zip files. For example, if you have a theme in the +file :file:`blue.zip`, you can put it right in the directory containing +:file:`conf.py` and use this configuration:: + + html_theme = "blue" + html_theme_path = ["."] + + +.. _builtin-themes: + +Builtin themes +-------------- + +Sphinx comes with a selection of themes to choose from: + +* **basic** -- This is a basically unstyled layout used as the base for the + *default* and *sphinxdoc* themes, and usable as the base for custom themes as + well. The HTML contains all important elements like sidebar and relation bar. + There is one option (which is inherited by *default* and *sphinxdoc*): + + - **nosidebar** (true or false): Don't include the sidebar. Defaults to + false. + +* **default** -- This is the default theme. It can be customized via these + options: + + - **rightsidebar** (true or false): Put the sidebar on the right side. + Defaults to false. + + - **stickysidebar** (true or false): Make the sidebar "fixed" so that it + doesn't scroll out of view for long body content. This may not work well + with all browsers. Defaults to false. + + There are also various color and font options that can change the color scheme + without having to write a custom stylesheet: + + - **footerbgcolor** (CSS color): Background color for the footer line. + - **footertextcolor** (CSS color): Text color for the footer line. + - **sidebarbgcolor** (CSS color): Background color for the sidebar. + - **sidebartextcolor** (CSS color): Text color for the sidebar. + - **sidebarlinkcolor** (CSS color): Link color for the sidebar. + - **relbarbgcolor** (CSS color): Background color for the relation bar. + - **relbartextcolor** (CSS color): Text color for the relation bar. + - **relbarlinkcolor** (CSS color): Link color for the relation bar. + - **bgcolor** (CSS color): Body background color. + - **textcolor** (CSS color): Body text color. + - **linkcolor** (CSS color): Body link color. + - **headbgcolor** (CSS color): Background color for headings. + - **headtextcolor** (CSS color): Text color for headings. + - **headlinkcolor** (CSS color): Link color for headings. + - **codebgcolor** (CSS color): Background color for code blocks. + - **codetextcolor** (CSS color): Default text color for code blocks, if not + set differently by the highlighting style. + + - **bodyfont** (CSS font-family): Font for normal text. + - **headfont** (CSS font-family): Font for headings. + +* **sphinxdoc** -- The theme used for this documentation. It features a sidebar + on the right side. There are currently no options beyond *nosidebar*. + +* **traditional** -- A theme resembling the old Python documentation. There are + currently no options beyond *nosidebar*. + + +Creating themes +--------------- + +As said, themes are either a directory or a zipfile (whose name is the theme +name), containing the following: + +* A :file:`theme.conf` file, see below. +* HTML templates, if needed. +* A ``static/`` directory containing any static files that will be copied to the + output statid directory on build. These can be images, styles, script files. + +The :file:`theme.conf` file is in INI format [1]_ (readable by the standard +Python :mod:`ConfigParser` module) and has the following structure: + +.. sourcecode:: ini + + [theme] + inherit = base theme + stylesheet = main CSS name + pygments_style = stylename + + [options] + variable = default value + +* The **inherit** setting gives the name of a "base theme", or ``none``. The + base theme will be used to locate missing templates (most themes will not have + to supply most templates if they use ``basic`` as the base theme), its options + will be inherited, and all of its static files will be used as well. + +* The **stylesheet** setting gives the name of a CSS file which will be + referenced in the HTML header. If you need more than one CSS file, either + include one from the other via CSS' ``@import``, or use a custom HTML template + that adds ``<link rel="stylesheet">`` tags as necessary. Setting the + :confval:`html_style` config value will override this setting. + +* The **pygments_style** setting gives the name of a Pygments style to use for + highlighting. This can be overridden by the user in the + :confval:`pygments_style` config value. + +* The **options** section contains pairs of variable names and default values. + These options can be overridden by the user in :confval:`html_theme_options` + and are accessible from all templates as ``theme_<name>``. + + +Templating +~~~~~~~~~~ + +The :doc:`guide to templating <templating>` is helpful if you want to write your +own templates. What is important to keep in mind is the order in which Sphinx +searches for templates: + +* First, in the user's ``templates_path`` directories. +* Then, in the selected theme. +* Then, in its base theme, its base's base theme, etc. + +When extending a template in the base theme with the same name, use the theme +name as an explicit directory: ``{% extends "basic/layout.html" %}``. From a +user ``templates_path`` template, you can still use the "exclamation mark" +syntax as described in the templating document. + + +Static templates +~~~~~~~~~~~~~~~~ + +Since theme options are meant for the user to configure a theme more easily, +without having to write a custom stylesheet, it is necessary to be able to +template static files as well as HTML files. Therefore, Sphinx supports +so-called "static templates", like this: + +If the name of a file in the ``static/`` directory of a theme (or in the user's +static path, for that matter) ends with ``_t``, it will be processed by the +template engine. The ``_t`` will be left from the final file name. For +example, the *default* theme has a file ``static/default.css_t`` which uses +templating to put the color options into the stylesheet. When a documentation +is built with the default theme, the output directory will contain a +``_static/default.css`` file where all template tags have been processed. + + +.. [1] It is not an executable Python file, as opposed to :file:`conf.py`, + because that would pose an unnecessary security risk if themes are + shared. + @@ -36,7 +36,7 @@ are already present, work fine and can be seen "in action" in the Python docs: and inclusion of appropriately formatted docstrings. ''' -requires = ['Pygments>=0.8', 'Jinja>=1.2', 'docutils>=0.4'] +requires = ['Pygments>=0.8', 'Jinja2>=2.1', 'docutils>=0.4'] if sys.version_info < (2, 4): print 'ERROR: Sphinx requires at least Python 2.4 to run.' @@ -98,7 +98,8 @@ else: else: for locale in os.listdir(self.directory): po_file = os.path.join(self.directory, locale, - 'LC_MESSAGES', self.domain + '.po') + 'LC_MESSAGES', + self.domain + '.po') if os.path.exists(po_file): po_files.append((locale, po_file)) js_files.append(os.path.join(self.directory, locale, @@ -177,7 +178,8 @@ setup( entry_points={ 'console_scripts': [ 'sphinx-build = sphinx:main', - 'sphinx-quickstart = sphinx.quickstart:main' + 'sphinx-quickstart = sphinx.quickstart:main', + 'sphinx-autogen = sphinx.ext.autosummary.generate:main', ], 'distutils.commands': [ 'build_sphinx = sphinx.setup_command:BuildDoc', diff --git a/sphinx-autogen.py b/sphinx-autogen.py new file mode 100755 index 00000000..494f4d85 --- /dev/null +++ b/sphinx-autogen.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Sphinx - Python documentation toolchain + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2007-2009 by Georg Brandl. + :license: BSD. +""" + +import sys + +if __name__ == '__main__': + from sphinx.ext.autosummary.generate import main + sys.exit(main(sys.argv)) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 0915f86b..0e37bf15 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -10,10 +10,13 @@ """ import sys +from os import path __revision__ = '$Revision$' -__version__ = '0.5.2b1' -__released__ = '0.5.2b1' +__version__ = '0.6b1' +__released__ = '0.6b1' + +package_dir = path.abspath(path.dirname(__file__)) def main(argv=sys.argv): @@ -28,17 +31,23 @@ def main(argv=sys.argv): errstr = str(err) if errstr.lower().startswith('no module named'): whichmod = errstr[16:] + hint = '' if whichmod.startswith('docutils'): whichmod = 'Docutils library' elif whichmod.startswith('jinja'): whichmod = 'Jinja library' elif whichmod == 'roman': whichmod = 'roman module (which is distributed with Docutils)' + hint = ('This can happen if you upgraded docutils using\n' + 'easy_install without uninstalling the old version' + 'first.') else: whichmod += ' module' print >>sys.stderr, \ 'Error: The %s cannot be found. Did you install Sphinx '\ 'and its dependencies correctly?' % whichmod + if hint: + print >> sys.stderr, hint return 1 raise return cmdline.main(argv) diff --git a/sphinx/_jinja.py b/sphinx/_jinja.py deleted file mode 100644 index 3adf0de8..00000000 --- a/sphinx/_jinja.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx._jinja - ~~~~~~~~~~~~~ - - Jinja glue. - - :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import codecs -from os import path - -from sphinx.util import mtimes_of_files -from sphinx.application import TemplateBridge - -from jinja import Environment -from jinja.loaders import BaseLoader -from jinja.exceptions import TemplateNotFound - - -def babel_extract(fileobj, keywords, comment_tags, options): - """ - Simple extractor to get some basic Babel support. - """ - env = Environment() - for lineno, sg, pl in env.get_translations_for_string(fileobj.read()): - yield lineno, None, (sg, pl), '' - - -class SphinxFileSystemLoader(BaseLoader): - """ - A loader that loads templates either relative to one of a list of given - paths, or from an absolute path. - """ - - def __init__(self, basepath, extpaths): - self.basepath = path.abspath(basepath) - self.extpaths = map(path.abspath, extpaths) - self.searchpaths = self.extpaths + [self.basepath] - - def get_source(self, environment, name, parent): - name = name.replace('/', path.sep) - if name.startswith('!'): - name = name[1:] - if not path.exists(path.join(self.basepath, name)): - raise TemplateNotFound(name) - filename = path.join(self.basepath, name) - elif path.isabs(name): - if not path.exists(name): - raise TemplateNotFound(name) - filename = name - else: - for searchpath in self.searchpaths: - if path.exists(path.join(searchpath, name)): - filename = path.join(searchpath, name) - break - else: - raise TemplateNotFound(name) - f = codecs.open(filename, 'r', environment.template_charset) - try: - return f.read() - finally: - f.close() - - -class TranslatorEnvironment(Environment): - class _Translator(object): - def __init__(self, translator): - self.trans = translator - - def gettext(self, string): - return self.trans.ugettext(string) - - def ngettext(self, singular, plural, n): - return self.trans.ungettext(singular, plural, n) - - def __init__(self, *args, **kwargs): - self.translator = kwargs['translator'] - del kwargs['translator'] - super(TranslatorEnvironment, self).__init__(*args, **kwargs) - - def get_translator(self, context): - return TranslatorEnvironment._Translator(self.translator) - - -class BuiltinTemplates(TemplateBridge): - def init(self, builder): - self.templates = {} - base_templates_path = path.join(path.dirname(__file__), 'templates') - ext_templates_path = [path.join(builder.confdir, dir) - for dir in builder.config.templates_path] - self.templates_path = [base_templates_path] + ext_templates_path - loader = SphinxFileSystemLoader(base_templates_path, ext_templates_path) - if builder.translator is not None: - self.jinja_env = TranslatorEnvironment(loader=loader, - friendly_traceback=False, translator=builder.translator) - else: - self.jinja_env = Environment(loader=loader, - # disable traceback, more likely that something - # in the application is broken than in the templates - friendly_traceback=False) - - def newest_template_mtime(self): - return max(mtimes_of_files(self.templates_path, '.html')) - - def render(self, template, context): - if template in self.templates: - return self.templates[template].render(context) - templateobj = self.templates[template] = \ - self.jinja_env.get_template(template) - return templateobj.render(context) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 6ea47d31..63907a0e 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -23,8 +23,12 @@ class desc(nodes.Admonition, nodes.Element): pass class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass # compatibility alias desc_classname = desc_addname -# return type (C); object type, e.g. -> annotation (Python) +# return type (C); object type class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): pass +# -> annotation (Python) +class desc_returns(desc_type): + def astext(self): + return ' -> ' + nodes.TextElement.astext(self) # main name of object class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): pass # argument list @@ -64,15 +68,25 @@ class pending_xref(nodes.Element): pass # compact paragraph -- never makes a <p> class compact_paragraph(nodes.paragraph): pass +# reference to a file to download +class download_reference(nodes.reference): pass + # for the ACKS list class acks(nodes.Element): pass +# for horizontal lists +class hlist(nodes.Element): pass +class hlistcol(nodes.Element): pass + # sets the highlighting language for literal blocks class highlightlang(nodes.Element): pass # like emphasis, but doesn't apply further text processors, e.g. smartypants class literal_emphasis(nodes.emphasis): pass +# for abbreviations (with explanations) +class abbreviation(nodes.Inline, nodes.TextElement): pass + # glossary class glossary(nodes.Element): pass @@ -85,13 +99,18 @@ class start_of_file(nodes.Element): pass # tabular column specification, used for the LaTeX writer class tabular_col_spec(nodes.Element): pass +# only (in/exclusion based on tags) +class only(nodes.Element): pass + # meta directive -- same as docutils' standard meta node, but pickleable class meta(nodes.Special, nodes.PreBibliographic, nodes.Element): pass # make them known to docutils. this is needed, because the HTML writer # will choke at some point if these are not added -nodes._add_node_class_names("""index desc desc_content desc_signature desc_type - desc_addname desc_name desc_parameterlist desc_parameter desc_optional +nodes._add_node_class_names("""index desc desc_content desc_signature + desc_type desc_returns desc_addname desc_name desc_parameterlist + desc_parameter desc_optional download_reference hlist hlistcol centered versionmodified seealso productionlist production toctree pending_xref compact_paragraph highlightlang literal_emphasis - glossary acks module start_of_file tabular_col_spec meta""".split()) + abbreviation glossary acks module start_of_file tabular_col_spec + meta""".split()) diff --git a/sphinx/application.py b/sphinx/application.py index 325cf9a5..e8e9fad6 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -7,58 +7,33 @@ Gracefully adapted from the TextPress system by Armin. - :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import sys +import types import posixpath from cStringIO import StringIO from docutils import nodes from docutils.parsers.rst import directives, roles -# create the error classes before importing the rest of Sphinx, so that -# they can be imported in a circular fashion - -class SphinxError(Exception): - """ - Base class for Sphinx errors that are shown to the user in a nicer - way than normal exceptions. - """ - category = 'Sphinx error' - -class ExtensionError(SphinxError): - """Raised if something's wrong with the configuration.""" - category = 'Extension error' - - def __init__(self, message, orig_exc=None): - super(ExtensionError, self).__init__(message) - self.orig_exc = orig_exc - - def __repr__(self): - if self.orig_exc: - return '%s(%r, %r)' % (self.__class__.__name__, - self.message, self.orig_exc) - return '%s(%r)' % (self.__class__.__name__, self.message) - - def __str__(self): - parent_str = super(ExtensionError, self).__str__() - if self.orig_exc: - return '%s (exception: %s)' % (parent_str, self.orig_exc) - return parent_str - - import sphinx from sphinx.roles import xfileref_role, innernodetypes from sphinx.config import Config -from sphinx.builder import builtin_builders, StandaloneHTMLBuilder -from sphinx.directives import desc_directive, target_directive, additional_xref_types +from sphinx.errors import SphinxError, SphinxWarning, ExtensionError +from sphinx.builders import BUILTIN_BUILDERS +from sphinx.directives import GenericDesc, Target, additional_xref_types from sphinx.environment import SphinxStandaloneReader +from sphinx.util.tags import Tags +from sphinx.util.compat import Directive, directive_dwim from sphinx.util.console import bold +# Directive is either new-style or old-style +clstypes = (type, types.ClassType) + # List of all known core events. Maps name to arguments description. events = { 'builder-inited': '', @@ -74,13 +49,16 @@ events = { CONFIG_FILENAME = 'conf.py' + class Sphinx(object): def __init__(self, srcdir, confdir, outdir, doctreedir, buildername, - confoverrides, status, warning=sys.stderr, freshenv=False): + confoverrides, status, warning=sys.stderr, freshenv=False, + warningiserror=False, tags=None): self.next_listener_id = 0 + self._extensions = {} self._listeners = {} - self.builderclasses = builtin_builders.copy() + self.builderclasses = BUILTIN_BUILDERS.copy() self.builder = None self.srcdir = srcdir @@ -94,19 +72,25 @@ class Sphinx(object): else: self._status = status self.quiet = False + if warning is None: self._warning = StringIO() else: self._warning = warning self._warncount = 0 + self.warningiserror = warningiserror self._events = events.copy() + # say hello to the world + self.info(bold('Running Sphinx v%s' % sphinx.__released__)) + # status code for command-line application self.statuscode = 0 # read config - self.config = Config(confdir, CONFIG_FILENAME, confoverrides) + self.tags = Tags(tags) + self.config = Config(confdir, CONFIG_FILENAME, confoverrides, self.tags) # load all extension modules for extension in self.config.extensions: @@ -124,11 +108,15 @@ class Sphinx(object): if buildername not in self.builderclasses: raise SphinxError('Builder name %s not registered' % buildername) - self.info(bold('Sphinx v%s, building %s' % (sphinx.__released__, - buildername))) - builderclass = self.builderclasses[buildername] + if isinstance(builderclass, tuple): + # builtin builder + mod, cls = builderclass + builderclass = getattr( + __import__('sphinx.builders.' + mod, None, None, [cls]), cls) self.builder = builderclass(self, freshenv=freshenv) + self.builder.tags = self.tags + self.builder.tags.add(self.builder.format) self.emit('builder-inited') def build(self, all_files, filenames): @@ -144,14 +132,19 @@ class Sphinx(object): raise else: self.emit('build-finished', None) + self.builder.cleanup() - def warn(self, message): + def warn(self, message, location=None, prefix='WARNING: '): + warntext = location and '%s: %s%s\n' % (location, prefix, message) or \ + '%s%s\n' % (prefix, message) + if self.warningiserror: + raise SphinxWarning(warntext) self._warncount += 1 try: - self._warning.write('WARNING: %s\n' % message) + self._warning.write(warntext) except UnicodeEncodeError: encoding = getattr(self._warning, 'encoding', 'ascii') - self._warning.write(('WARNING: %s\n' % message).encode(encoding, 'replace')) + self._warning.write(warntext.encode(encoding, 'replace')) def info(self, message='', nonl=False): try: @@ -166,13 +159,17 @@ class Sphinx(object): # general extensibility interface def setup_extension(self, extension): - """Import and setup a Sphinx extension module.""" + """Import and setup a Sphinx extension module. No-op if called twice.""" + if extension in self._extensions: + return try: mod = __import__(extension, None, None, ['setup']) except ImportError, err: - raise ExtensionError('Could not import extension %s' % extension, err) + raise ExtensionError('Could not import extension %s' % extension, + err) if hasattr(mod, 'setup'): mod.setup(self) + self._extensions[extension] = mod def import_object(self, objname, source=None): """Import an object from a 'module.name' string.""" @@ -180,15 +177,18 @@ class Sphinx(object): module, name = objname.rsplit('.', 1) except ValueError, err: raise ExtensionError('Invalid full object name %s' % objname + - (source and ' (needed for %s)' % source or ''), err) + (source and ' (needed for %s)' % source or ''), + err) try: return getattr(__import__(module, None, None, [name]), name) except ImportError, err: raise ExtensionError('Could not import %s' % module + - (source and ' (needed for %s)' % source or ''), err) + (source and ' (needed for %s)' % source or ''), + err) except AttributeError, err: raise ExtensionError('Could not find %s' % objname + - (source and ' (needed for %s)' % source or ''), err) + (source and ' (needed for %s)' % source or ''), + err) # event interface @@ -228,16 +228,24 @@ class Sphinx(object): def add_builder(self, builder): if not hasattr(builder, 'name'): - raise ExtensionError('Builder class %s has no "name" attribute' % builder) + raise ExtensionError('Builder class %s has no "name" attribute' + % builder) if builder.name in self.builderclasses: - raise ExtensionError('Builder %r already exists (in module %s)' % ( - builder.name, self.builderclasses[builder.name].__module__)) + if isinstance(self.builderclasses[builder.name], tuple): + raise ExtensionError('Builder %r is a builtin builder' % + builder.name) + else: + raise ExtensionError( + 'Builder %r already exists (in module %s)' % ( + builder.name, self.builderclasses[builder.name].__module__)) self.builderclasses[builder.name] = builder - def add_config_value(self, name, default, rebuild_env): + def add_config_value(self, name, default, rebuild): if name in self.config.values: raise ExtensionError('Config value %r already present' % name) - self.config.values[name] = (default, rebuild_env) + if rebuild in (False, True): + rebuild = rebuild and 'env' or '' + self.config.values[name] = (default, rebuild) def add_event(self, name): if name in self._events: @@ -250,14 +258,14 @@ class Sphinx(object): try: visit, depart = val except ValueError: - raise ExtensionError('Value for key %r must be a (visit, depart) ' - 'function tuple' % key) + raise ExtensionError('Value for key %r must be a ' + '(visit, depart) function tuple' % key) if key == 'html': - from sphinx.htmlwriter import HTMLTranslator as translator + from sphinx.writers.html import HTMLTranslator as translator elif key == 'latex': - from sphinx.latexwriter import LaTeXTranslator as translator + from sphinx.writers.latex import LaTeXTranslator as translator elif key == 'text': - from sphinx.textwriter import TextTranslator as translator + from sphinx.writers.text import TextTranslator as translator else: # ignore invalid keys for compatibility continue @@ -265,19 +273,33 @@ class Sphinx(object): if depart: setattr(translator, 'depart_'+node.__name__, depart) - def add_directive(self, name, func, content, arguments, **options): - func.content = content - func.arguments = arguments - func.options = options - directives.register_directive(name, func) + def add_directive(self, name, obj, content=None, arguments=None, **options): + if isinstance(obj, clstypes) and issubclass(obj, Directive): + if content or arguments or options: + raise ExtensionError('when adding directive classes, no ' + 'additional arguments may be given') + directives.register_directive(name, directive_dwim(obj)) + else: + obj.content = content + obj.arguments = arguments + obj.options = options + directives.register_directive(name, obj) def add_role(self, name, role): roles.register_local_role(name, role) + def add_generic_role(self, name, nodeclass): + # don't use roles.register_generic_role because it uses + # register_canonical_role + role = roles.GenericRole(name, nodeclass) + roles.register_local_role(name, role) + def add_description_unit(self, directivename, rolename, indextemplate='', parse_node=None, ref_nodeclass=None): - additional_xref_types[directivename] = (rolename, indextemplate, parse_node) - directives.register_directive(directivename, desc_directive) + additional_xref_types[directivename] = (rolename, indextemplate, + parse_node) + directives.register_directive(directivename, + directive_dwim(GenericDesc)) roles.register_local_role(rolename, xfileref_role) if ref_nodeclass is not None: innernodetypes[rolename] = ref_nodeclass @@ -285,7 +307,7 @@ class Sphinx(object): def add_crossref_type(self, directivename, rolename, indextemplate='', ref_nodeclass=None): additional_xref_types[directivename] = (rolename, indextemplate, None) - directives.register_directive(directivename, target_directive) + directives.register_directive(directivename, directive_dwim(Target)) roles.register_local_role(rolename, xfileref_role) if ref_nodeclass is not None: innernodetypes[rolename] = ref_nodeclass @@ -294,9 +316,25 @@ class Sphinx(object): SphinxStandaloneReader.transforms.append(transform) def add_javascript(self, filename): + from sphinx.builders.html import StandaloneHTMLBuilder StandaloneHTMLBuilder.script_files.append( posixpath.join('_static', filename)) + def add_lexer(self, alias, lexer): + from sphinx.highlighting import lexers + if lexers is None: + return + lexers[alias] = lexer + + def add_autodocumenter(self, cls): + from sphinx.ext import autodoc + autodoc.add_documenter(cls) + self.add_directive('auto' + cls.objtype, autodoc.AutoDirective) + + def add_autodoc_attrgetter(self, type, getter): + from sphinx.ext import autodoc + autodoc.AutoDirective._special_attrgetters[type] = getter + class TemplateBridge(object): """ @@ -304,11 +342,15 @@ class TemplateBridge(object): that renders templates given a template name and a context. """ - def init(self, builder): + def init(self, builder, theme=None, dirs=None): """ - Called by the builder to initialize the template system. *builder* - is the builder object; you'll probably want to look at the value of - ``builder.config.templates_path``. + Called by the builder to initialize the template system. + + *builder* is the builder object; you'll probably want to look at the + value of ``builder.config.templates_path``. + + *theme* is a :class:`sphinx.theming.Theme` object or None; in the latter + case, *dirs* can be list of fixed directories to look for templates. """ raise NotImplementedError('must be implemented in subclasses') @@ -322,7 +364,14 @@ class TemplateBridge(object): def render(self, template, context): """ - Called by the builder to render a *template* with a specified - context (a Python dictionary). + Called by the builder to render a template given as a filename with a + specified context (a Python dictionary). + """ + raise NotImplementedError('must be implemented in subclasses') + + def render_string(self, template, context): + """ + Called by the builder to render a template given as a string with a + specified context (a Python dictionary). """ raise NotImplementedError('must be implemented in subclasses') diff --git a/sphinx/builder.py b/sphinx/builder.py index 7b1f8bb1..13c56e18 100644 --- a/sphinx/builder.py +++ b/sphinx/builder.py @@ -3,1275 +3,26 @@ sphinx.builder ~~~~~~~~~~~~~~ - Builder classes for different output formats. + .. warning:: + + This module is only kept for API compatibility; new code should + import these classes directly from the sphinx.builders package. :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -import os -import time -import codecs -import shutil -import gettext -import cPickle as pickle -from os import path -from cgi import escape - -from docutils import nodes -from docutils.io import StringOutput, FileOutput, DocTreeInput -from docutils.core import publish_parts -from docutils.utils import new_document -from docutils.frontend import OptionParser -from docutils.readers.doctree import Reader as DoctreeReader - -from sphinx import addnodes, locale, __version__ -from sphinx.util import movefile, ensuredir, relative_uri, SEP, os_path, texescape, \ - ustrftime -from sphinx.htmlhelp import build_hhx -from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator -from sphinx.textwriter import TextWriter -from sphinx.latexwriter import LaTeXWriter -from sphinx.environment import BuildEnvironment, NoUri -from sphinx.highlighting import PygmentsBridge -from sphinx.util.console import bold, purple, darkgreen -from sphinx.search import js_index -from sphinx.application import SphinxError - -try: - import json -except ImportError: - try: - import simplejson as json - except ImportError: - json = None - -# side effect: registers roles and directives -from sphinx import roles -from sphinx import directives - -ENV_PICKLE_FILENAME = 'environment.pickle' -LAST_BUILD_FILENAME = 'last_build' -INVENTORY_FILENAME = 'objects.inv' - - -class Builder(object): - """ - Builds target formats from the reST sources. - """ - - # builder's name, for the -b command line options - name = '' - - def __init__(self, app, env=None, freshenv=False): - self.srcdir = app.srcdir - self.confdir = app.confdir - self.outdir = app.outdir - self.doctreedir = app.doctreedir - if not path.isdir(self.doctreedir): - os.makedirs(self.doctreedir) - - self.app = app - self.warn = app.warn - self.info = app.info - self.config = app.config - - self.load_i18n() - - # images that need to be copied over (source -> dest) - self.images = {} - - # if None, this is set in load_env() - self.env = env - self.freshenv = freshenv - - self.init() - self.load_env() - - # helper methods - - def init(self): - """Load necessary templates and perform initialization.""" - raise NotImplementedError - - def init_templates(self): - # Call this from init() if you need templates. - if self.config.template_bridge: - self.templates = self.app.import_object( - self.config.template_bridge, 'template_bridge setting')() - else: - from sphinx._jinja import BuiltinTemplates - self.templates = BuiltinTemplates() - self.templates.init(self) - - def get_target_uri(self, docname, typ=None): - """ - Return the target URI for a document name (typ can be used to qualify - the link characteristic for individual builders). - """ - raise NotImplementedError - - def get_relative_uri(self, from_, to, typ=None): - """ - Return a relative URI between two source filenames. May raise environment.NoUri - if there's no way to return a sensible URI. - """ - return relative_uri(self.get_target_uri(from_), - self.get_target_uri(to, typ)) - - def get_outdated_docs(self): - """ - Return an iterable of output files that are outdated, or a string describing - what an update build will build. - """ - raise NotImplementedError - - def status_iterator(self, iterable, summary, colorfunc=darkgreen): - l = -1 - for item in iterable: - if l == -1: - self.info(bold(summary), nonl=1) - l = 0 - self.info(colorfunc(item) + ' ', nonl=1) - yield item - if l == 0: - self.info() - - supported_image_types = [] - - def post_process_images(self, doctree): - """ - Pick the best candidate for all image URIs. - """ - for node in doctree.traverse(nodes.image): - if '?' in node['candidates']: - # don't rewrite nonlocal image URIs - continue - if '*' not in node['candidates']: - for imgtype in self.supported_image_types: - candidate = node['candidates'].get(imgtype, None) - if candidate: - break - else: - self.warn('%s:%s: no matching candidate for image URI %r' % - (node.source, getattr(node, 'lineno', ''), node['uri'])) - continue - node['uri'] = candidate - else: - candidate = node['uri'] - if candidate not in self.env.images: - # non-existing URI; let it alone - continue - self.images[candidate] = self.env.images[candidate][1] - - # build methods - - def load_i18n(self): - """ - Load translated strings from the configured localedirs if - enabled in the configuration. - """ - self.translator = None - if self.config.language is not None: - self.info(bold('loading translations [%s]... ' % self.config.language), - nonl=True) - locale_dirs = [path.join(path.dirname(__file__), 'locale')] + \ - [path.join(self.srcdir, x) for x in self.config.locale_dirs] - for dir_ in locale_dirs: - try: - trans = gettext.translation('sphinx', localedir=dir_, - languages=[self.config.language]) - if self.translator is None: - self.translator = trans - else: - self.translator._catalog.update(trans.catalog) - except Exception: - # Language couldn't be found in the specified path - pass - if self.translator is not None: - self.info('done') - else: - self.info('locale not available') - if self.translator is None: - self.translator = gettext.NullTranslations() - self.translator.install(unicode=True) - locale.init() # translate common labels - - def load_env(self): - """Set up the build environment.""" - if self.env: - return - if not self.freshenv: - try: - self.info(bold('loading pickled environment... '), nonl=True) - self.env = BuildEnvironment.frompickle(self.config, - path.join(self.doctreedir, ENV_PICKLE_FILENAME)) - self.info('done') - except Exception, err: - if type(err) is IOError and err.errno == 2: - self.info('not found') - else: - self.info('failed: %s' % err) - self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config) - self.env.find_files(self.config) - else: - self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config) - self.env.find_files(self.config) - self.env.set_warnfunc(self.warn) - - def build_all(self): - """Build all source files.""" - self.build(None, summary='all source files', method='all') - - def build_specific(self, filenames): - """Only rebuild as much as needed for changes in the source_filenames.""" - # bring the filenames to the canonical format, that is, - # relative to the source directory and without source_suffix. - dirlen = len(self.srcdir) + 1 - to_write = [] - suffix = self.config.source_suffix - for filename in filenames: - filename = path.abspath(filename)[dirlen:] - if filename.endswith(suffix): - filename = filename[:-len(suffix)] - filename = filename.replace(os.path.sep, SEP) - to_write.append(filename) - self.build(to_write, method='specific', - summary='%d source files given on command ' - 'line' % len(to_write)) - - def build_update(self): - """Only rebuild files changed or added since last build.""" - to_build = self.get_outdated_docs() - if isinstance(to_build, str): - self.build(['__all__'], to_build) - else: - to_build = list(to_build) - self.build(to_build, - summary='targets for %d source files that are ' - 'out of date' % len(to_build)) - - def build(self, docnames, summary=None, method='update'): - if summary: - self.info(bold('building [%s]: ' % self.name), nonl=1) - self.info(summary) - - updated_docnames = [] - # while reading, collect all warnings from docutils - warnings = [] - self.env.set_warnfunc(warnings.append) - self.info(bold('updating environment: '), nonl=1) - iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app) - # the first item in the iterator is a summary message - self.info(iterator.next()) - for docname in self.status_iterator(iterator, 'reading sources... ', purple): - updated_docnames.append(docname) - # nothing further to do, the environment has already done the reading - for warning in warnings: - if warning.strip(): - self.warn(warning) - self.env.set_warnfunc(self.warn) - - if updated_docnames: - # save the environment - self.info(bold('pickling environment... '), nonl=True) - self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME)) - self.info('done') - - # global actions - self.info(bold('checking consistency... '), nonl=True) - self.env.check_consistency() - self.info('done') - else: - if method == 'update' and not docnames: - self.info(bold('no targets are out of date.')) - return - - # another indirection to support methods which don't build files - # individually - self.write(docnames, updated_docnames, method) - - # finish (write static files etc.) - self.finish() - status = self.app.statuscode == 0 and 'succeeded' or 'finished with problems' - if self.app._warncount: - self.info(bold('build %s, %s warning%s.' % - (status, self.app._warncount, - self.app._warncount != 1 and 's' or ''))) - else: - self.info(bold('build %s.' % status)) - - def write(self, build_docnames, updated_docnames, method='update'): - if build_docnames is None or build_docnames == ['__all__']: - # build_all - build_docnames = self.env.found_docs - if method == 'update': - # build updated ones as well - docnames = set(build_docnames) | set(updated_docnames) - else: - docnames = set(build_docnames) - - # add all toctree-containing files that may have changed - for docname in list(docnames): - for tocdocname in self.env.files_to_rebuild.get(docname, []): - docnames.add(tocdocname) - docnames.add(self.config.master_doc) - - self.info(bold('preparing documents... '), nonl=True) - self.prepare_writing(docnames) - self.info('done') - - # write target files - warnings = [] - self.env.set_warnfunc(warnings.append) - for docname in self.status_iterator(sorted(docnames), - 'writing output... ', darkgreen): - doctree = self.env.get_and_resolve_doctree(docname, self) - self.write_doc(docname, doctree) - for warning in warnings: - if warning.strip(): - self.warn(warning) - self.env.set_warnfunc(self.warn) - - def prepare_writing(self, docnames): - raise NotImplementedError - - def write_doc(self, docname, doctree): - raise NotImplementedError - - def finish(self): - raise NotImplementedError - - -class StandaloneHTMLBuilder(Builder): - """ - Builds standalone HTML docs. - """ - name = 'html' - copysource = True - out_suffix = '.html' - indexer_format = js_index - supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', - 'image/jpeg'] - searchindex_filename = 'searchindex.js' - add_header_links = True - add_definition_links = True - - # This is a class attribute because it is mutated by Sphinx.add_javascript. - script_files = ['_static/jquery.js', '_static/doctools.js'] - - def init(self): - """Load templates.""" - self.init_templates() - self.init_translator_class() - if self.config.html_file_suffix: - self.out_suffix = self.config.html_file_suffix - - if self.config.language is not None: - jsfile = path.join(path.dirname(__file__), 'locale', self.config.language, - 'LC_MESSAGES', 'sphinx.js') - if path.isfile(jsfile): - self.script_files.append('_static/translations.js') - - def init_translator_class(self): - if self.config.html_translator_class: - self.translator_class = self.app.import_object( - self.config.html_translator_class, 'html_translator_class setting') - elif self.config.html_use_smartypants: - self.translator_class = SmartyPantsHTMLTranslator - else: - self.translator_class = HTMLTranslator - - def render_partial(self, node): - """Utility: Render a lone doctree node.""" - doc = new_document('<partial node>') - doc.append(node) - return publish_parts( - doc, - source_class=DocTreeInput, - reader=DoctreeReader(), - writer=HTMLWriter(self), - settings_overrides={'output_encoding': 'unicode'} - ) - - def prepare_writing(self, docnames): - from sphinx.search import IndexBuilder - - self.indexer = IndexBuilder(self.env) - self.load_indexer(docnames) - self.docwriter = HTMLWriter(self) - self.docsettings = OptionParser( - defaults=self.env.settings, - components=(self.docwriter,)).get_default_values() - - # format the "last updated on" string, only once is enough since it - # typically doesn't include the time of day - lufmt = self.config.html_last_updated_fmt - if lufmt is not None: - self.last_updated = ustrftime(lufmt or _('%b %d, %Y')) - else: - self.last_updated = None - - logo = self.config.html_logo and \ - path.basename(self.config.html_logo) or '' - - favicon = self.config.html_favicon and \ - path.basename(self.config.html_favicon) or '' - if favicon and os.path.splitext(favicon)[1] != '.ico': - self.warn('html_favicon is not an .ico file') - - if not isinstance(self.config.html_use_opensearch, basestring): - self.warn('html_use_opensearch config value must now be a string') - - self.relations = self.env.collect_relations() - - rellinks = [] - if self.config.html_use_index: - rellinks.append(('genindex', _('General Index'), 'I', _('index'))) - if self.config.html_use_modindex and self.env.modules: - rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules'))) - - self.globalcontext = dict( - project = self.config.project, - release = self.config.release, - version = self.config.version, - last_updated = self.last_updated, - copyright = self.config.copyright, - master_doc = self.config.master_doc, - style = self.config.html_style, - use_opensearch = self.config.html_use_opensearch, - docstitle = self.config.html_title, - shorttitle = self.config.html_short_title, - show_sphinx = self.config.html_show_sphinx, - has_source = self.config.html_copy_source, - file_suffix = self.out_suffix, - script_files = self.script_files, - sphinx_version = __version__, - rellinks = rellinks, - builder = self.name, - parents = [], - logo = logo, - favicon = favicon, - ) - self.globalcontext.update(self.config.html_context) - - def get_doc_context(self, docname, body, metatags): - """Collect items for the template context of a page.""" - # find out relations - prev = next = None - parents = [] - rellinks = self.globalcontext['rellinks'][:] - related = self.relations.get(docname) - titles = self.env.titles - if related and related[2]: - try: - next = {'link': self.get_relative_uri(docname, related[2]), - 'title': self.render_partial(titles[related[2]])['title']} - rellinks.append((related[2], next['title'], 'N', _('next'))) - except KeyError: - next = None - if related and related[1]: - try: - prev = {'link': self.get_relative_uri(docname, related[1]), - 'title': self.render_partial(titles[related[1]])['title']} - rellinks.append((related[1], prev['title'], 'P', _('previous'))) - except KeyError: - # the relation is (somehow) not in the TOC tree, handle that gracefully - prev = None - while related and related[0]: - try: - parents.append( - {'link': self.get_relative_uri(docname, related[0]), - 'title': self.render_partial(titles[related[0]])['title']}) - except KeyError: - pass - related = self.relations.get(related[0]) - if parents: - parents.pop() # remove link to the master file; we have a generic - # "back to index" link already - parents.reverse() - - # title rendered as HTML - title = titles.get(docname) - title = title and self.render_partial(title)['title'] or '' - # the name for the copied source - sourcename = self.config.html_copy_source and docname + '.txt' or '' - - # metadata for the document - meta = self.env.metadata.get(docname) - - return dict( - parents = parents, - prev = prev, - next = next, - title = title, - meta = meta, - body = body, - metatags = metatags, - rellinks = rellinks, - sourcename = sourcename, - toc = self.render_partial(self.env.get_toc_for(docname))['fragment'], - # only display a TOC if there's more than one item to show - display_toc = (self.env.toc_num_entries[docname] > 1), - ) - - def write_doc(self, docname, doctree): - self.post_process_images(doctree) - destination = StringOutput(encoding='utf-8') - doctree.settings = self.docsettings - - self.imgpath = relative_uri(self.get_target_uri(docname), '_images') - self.docwriter.write(doctree, destination) - self.docwriter.assemble_parts() - body = self.docwriter.parts['fragment'] - metatags = self.docwriter.clean_meta - - ctx = self.get_doc_context(docname, body, metatags) - self.index_page(docname, doctree, ctx.get('title', '')) - self.handle_page(docname, ctx, event_arg=doctree) - - def finish(self): - self.info(bold('writing additional files...'), nonl=1) - - # the global general index - - if self.config.html_use_index: - # the total count of lines for each index letter, used to distribute - # the entries into two columns - genindex = self.env.create_index(self) - indexcounts = [] - for _, entries in genindex: - indexcounts.append(sum(1 + len(subitems) - for _, (_, subitems) in entries)) - - genindexcontext = dict( - genindexentries = genindex, - genindexcounts = indexcounts, - split_index = self.config.html_split_index, - ) - self.info(' genindex', nonl=1) - - if self.config.html_split_index: - self.handle_page('genindex', genindexcontext, 'genindex-split.html') - self.handle_page('genindex-all', genindexcontext, 'genindex.html') - for (key, entries), count in zip(genindex, indexcounts): - ctx = {'key': key, 'entries': entries, 'count': count, - 'genindexentries': genindex} - self.handle_page('genindex-' + key, ctx, 'genindex-single.html') - else: - self.handle_page('genindex', genindexcontext, 'genindex.html') - - # the global module index - - if self.config.html_use_modindex and self.env.modules: - # the sorted list of all modules, for the global module index - modules = sorted(((mn, (self.get_relative_uri('modindex', fn) + - '#module-' + mn, sy, pl, dep)) - for (mn, (fn, sy, pl, dep)) in - self.env.modules.iteritems()), - key=lambda x: x[0].lower()) - # collect all platforms - platforms = set() - # sort out collapsable modules - modindexentries = [] - letters = [] - pmn = '' - num_toplevels = 0 - num_collapsables = 0 - cg = 0 # collapse group - fl = '' # first letter - for mn, (fn, sy, pl, dep) in modules: - pl = pl and pl.split(', ') or [] - platforms.update(pl) - if fl != mn[0].lower() and mn[0] != '_': - # heading - modindexentries.append(['', False, 0, False, - mn[0].upper(), '', [], False]) - letters.append(mn[0].upper()) - tn = mn.split('.')[0] - if tn != mn: - # submodule - if pmn == tn: - # first submodule - make parent collapsable - modindexentries[-1][1] = True - num_collapsables += 1 - elif not pmn.startswith(tn): - # submodule without parent in list, add dummy entry - cg += 1 - modindexentries.append([tn, True, cg, False, '', '', [], False]) - else: - num_toplevels += 1 - cg += 1 - modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep]) - pmn = mn - fl = mn[0].lower() - platforms = sorted(platforms) - - # apply heuristics when to collapse modindex at page load: - # only collapse if number of toplevel modules is larger than - # number of submodules - collapse = len(modules) - num_toplevels < num_toplevels - - modindexcontext = dict( - modindexentries = modindexentries, - platforms = platforms, - letters = letters, - collapse_modindex = collapse, - ) - self.info(' modindex', nonl=1) - self.handle_page('modindex', modindexcontext, 'modindex.html') - - # the search page - if self.name != 'htmlhelp': - self.info(' search', nonl=1) - self.handle_page('search', {}, 'search.html') - - # additional pages from conf.py - for pagename, template in self.config.html_additional_pages.items(): - self.info(' '+pagename, nonl=1) - self.handle_page(pagename, {}, template) - - if self.config.html_use_opensearch and self.name != 'htmlhelp': - self.info(' opensearch', nonl=1) - fn = path.join(self.outdir, '_static', 'opensearch.xml') - self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn) - - self.info() - - # copy image files - if self.images: - self.info(bold('copying images...'), nonl=True) - ensuredir(path.join(self.outdir, '_images')) - for src, dest in self.images.iteritems(): - self.info(' '+src, nonl=1) - shutil.copyfile(path.join(self.srcdir, src), - path.join(self.outdir, '_images', dest)) - self.info() - - # copy static files - self.info(bold('copying static files... '), nonl=True) - ensuredir(path.join(self.outdir, '_static')) - # first, create pygments style file - f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w') - f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet()) - f.close() - # then, copy translations JavaScript file - if self.config.language is not None: - jsfile = path.join(path.dirname(__file__), 'locale', self.config.language, - 'LC_MESSAGES', 'sphinx.js') - if path.isfile(jsfile): - shutil.copyfile(jsfile, path.join(self.outdir, '_static', - 'translations.js')) - # then, copy over all user-supplied static files - staticdirnames = [path.join(path.dirname(__file__), 'static')] + \ - [path.join(self.confdir, spath) - for spath in self.config.html_static_path] - for staticdirname in staticdirnames: - for filename in os.listdir(staticdirname): - if filename.startswith('.'): - continue - fullname = path.join(staticdirname, filename) - targetname = path.join(self.outdir, '_static', filename) - if path.isfile(fullname): - shutil.copyfile(fullname, targetname) - elif path.isdir(fullname): - if filename in self.config.exclude_dirnames: - continue - if path.exists(targetname): - shutil.rmtree(targetname) - shutil.copytree(fullname, targetname) - # last, copy logo file (handled differently) - if self.config.html_logo: - logobase = path.basename(self.config.html_logo) - shutil.copyfile(path.join(self.confdir, self.config.html_logo), - path.join(self.outdir, '_static', logobase)) - self.info('done') - - # dump the search index - self.handle_finish() - - def get_outdated_docs(self): - if self.templates: - template_mtime = self.templates.newest_template_mtime() - else: - template_mtime = 0 - for docname in self.env.found_docs: - if docname not in self.env.all_docs: - yield docname - continue - targetname = self.env.doc2path(docname, self.outdir, self.out_suffix) - try: - targetmtime = path.getmtime(targetname) - except Exception: - targetmtime = 0 - try: - srcmtime = max(path.getmtime(self.env.doc2path(docname)), - template_mtime) - if srcmtime > targetmtime: - yield docname - except EnvironmentError: - # source doesn't exist anymore - pass - - def load_indexer(self, docnames): - keep = set(self.env.all_docs) - set(docnames) - try: - f = open(path.join(self.outdir, self.searchindex_filename), 'rb') - try: - self.indexer.load(f, self.indexer_format) - finally: - f.close() - except (IOError, OSError, ValueError): - if keep: - self.warn("search index couldn't be loaded, but not all documents " - "will be built: the index will be incomplete.") - # delete all entries for files that will be rebuilt - self.indexer.prune(keep) - - def index_page(self, pagename, doctree, title): - # only index pages with title - if self.indexer is not None and title: - self.indexer.feed(pagename, title, doctree) - - # --------- these are overwritten by the serialization builder - - def get_target_uri(self, docname, typ=None): - return docname + self.out_suffix - - def handle_page(self, pagename, addctx, templatename='page.html', - outfilename=None, event_arg=None): - ctx = self.globalcontext.copy() - # current_page_name is backwards compatibility - ctx['pagename'] = ctx['current_page_name'] = pagename - - def pathto(otheruri, resource=False, - baseuri=self.get_target_uri(pagename)): - if not resource: - otheruri = self.get_target_uri(otheruri) - return relative_uri(baseuri, otheruri) - ctx['pathto'] = pathto - ctx['hasdoc'] = lambda name: name in self.env.all_docs - ctx['customsidebar'] = self.config.html_sidebars.get(pagename) - ctx.update(addctx) - - self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) - - output = self.templates.render(templatename, ctx) - if not outfilename: - outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix) - ensuredir(path.dirname(outfilename)) # normally different from self.outdir - try: - f = codecs.open(outfilename, 'w', 'utf-8') - try: - f.write(output) - finally: - f.close() - except (IOError, OSError), err: - self.warn("Error writing file %s: %s" % (outfilename, err)) - if self.copysource and ctx.get('sourcename'): - # copy the source file for the "show source" link - source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename'])) - ensuredir(path.dirname(source_name)) - shutil.copyfile(self.env.doc2path(pagename), source_name) - - def handle_finish(self): - self.info(bold('dumping search index... '), nonl=True) - self.indexer.prune(self.env.all_docs) - searchindexfn = path.join(self.outdir, self.searchindex_filename) - # first write to a temporary file, so that if dumping fails, the existing - # index won't be overwritten - f = open(searchindexfn + '.tmp', 'wb') - try: - self.indexer.dump(f, self.indexer_format) - finally: - f.close() - movefile(searchindexfn + '.tmp', searchindexfn) - self.info('done') - - self.info(bold('dumping object inventory... '), nonl=True) - f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') - try: - f.write('# Sphinx inventory version 1\n') - f.write('# Project: %s\n' % self.config.project.encode('utf-8')) - f.write('# Version: %s\n' % self.config.version) - for modname, info in self.env.modules.iteritems(): - f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) - for refname, (docname, desctype) in self.env.descrefs.iteritems(): - f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname))) - finally: - f.close() - self.info('done') - - -class SerializingHTMLBuilder(StandaloneHTMLBuilder): - """ - An abstract builder that serializes the HTML generated. - """ - #: the serializing implementation to use. Set this to a module that - #: implements a `dump`, `load`, `dumps` and `loads` functions - #: (pickle, simplejson etc.) - implementation = None - - #: the filename for the global context file - globalcontext_filename = None - - supported_image_types = ('image/svg+xml', 'image/png', 'image/gif', - 'image/jpeg') - - def init(self): - self.init_translator_class() - self.templates = None # no template bridge necessary - - def get_target_uri(self, docname, typ=None): - if docname == 'index': - return '' - if docname.endswith(SEP + 'index'): - return docname[:-5] # up to sep - return docname + SEP - - def handle_page(self, pagename, ctx, templatename='page.html', - outfilename=None, event_arg=None): - ctx['current_page_name'] = pagename - sidebarfile = self.config.html_sidebars.get(pagename) - if sidebarfile: - ctx['customsidebar'] = sidebarfile - - if not outfilename: - outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix) - - self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) - - ensuredir(path.dirname(outfilename)) - f = open(outfilename, 'wb') - try: - self.implementation.dump(ctx, f, 2) - finally: - f.close() - - # if there is a source file, copy the source file for the - # "show source" link - if ctx.get('sourcename'): - source_name = path.join(self.outdir, '_sources', - os_path(ctx['sourcename'])) - ensuredir(path.dirname(source_name)) - shutil.copyfile(self.env.doc2path(pagename), source_name) - - def handle_finish(self): - # dump the global context - outfilename = path.join(self.outdir, self.globalcontext_filename) - f = open(outfilename, 'wb') - try: - self.implementation.dump(self.globalcontext, f, 2) - finally: - f.close() - - # super here to dump the search index - StandaloneHTMLBuilder.handle_finish(self) - - # copy the environment file from the doctree dir to the output dir - # as needed by the web app - shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME), - path.join(self.outdir, ENV_PICKLE_FILENAME)) - - # touch 'last build' file, used by the web application to determine - # when to reload its environment and clear the cache - open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close() - - -class PickleHTMLBuilder(SerializingHTMLBuilder): - """ - A Builder that dumps the generated HTML into pickle files. - """ - implementation = pickle - indexer_format = pickle - name = 'pickle' - out_suffix = '.fpickle' - globalcontext_filename = 'globalcontext.pickle' - searchindex_filename = 'searchindex.pickle' - - -class JSONHTMLBuilder(SerializingHTMLBuilder): - """ - A builder that dumps the generated HTML into JSON files. - """ - implementation = json - indexer_format = json - name = 'json' - out_suffix = '.fjson' - globalcontext_filename = 'globalcontext.json' - searchindex_filename = 'searchindex.json' - - def init(self): - if json is None: - raise SphinxError('The module simplejson (or json in Python >= 2.6) ' - 'is not available. The JSONHTMLBuilder builder ' - 'will not work.') - SerializingHTMLBuilder.init(self) - - -class HTMLHelpBuilder(StandaloneHTMLBuilder): - """ - Builder that also outputs Windows HTML help project, contents and index files. - Adapted from the original Doc/tools/prechm.py. - """ - name = 'htmlhelp' - - # don't copy the reST source - copysource = False - supported_image_types = ['image/png', 'image/gif', 'image/jpeg'] - - # don't add links - add_header_links = False - add_definition_links = False - - def init(self): - StandaloneHTMLBuilder.init(self) - # the output files for HTML help must be .html only - self.out_suffix = '.html' - - def handle_finish(self): - build_hhx(self, self.outdir, self.config.htmlhelp_basename) - - -class LaTeXBuilder(Builder): - """ - Builds LaTeX output to create PDF. - """ - name = 'latex' - supported_image_types = ['application/pdf', 'image/png', 'image/gif', - 'image/jpeg'] - - def init(self): - self.docnames = [] - self.document_data = [] - texescape.init() - - def get_outdated_docs(self): - return 'all documents' # for now - - def get_target_uri(self, docname, typ=None): - if typ == 'token': - # token references are always inside production lists and must be - # replaced by \token{} in LaTeX - return '@token' - if docname not in self.docnames: - raise NoUri - else: - return '' - - def init_document_data(self): - preliminary_document_data = map(list, self.config.latex_documents) - if not preliminary_document_data: - self.warn('No "latex_documents" config value found; no documents ' - 'will be written.') - return - # assign subdirs to titles - self.titles = [] - for entry in preliminary_document_data: - docname = entry[0] - if docname not in self.env.all_docs: - self.warn('"latex_documents" config value references unknown ' - 'document %s' % docname) - continue - self.document_data.append(entry) - if docname.endswith(SEP+'index'): - docname = docname[:-5] - self.titles.append((docname, entry[2])) - - def write(self, *ignored): - docwriter = LaTeXWriter(self) - docsettings = OptionParser( - defaults=self.env.settings, - components=(docwriter,)).get_default_values() - - self.init_document_data() - - for entry in self.document_data: - docname, targetname, title, author, docclass = entry[:5] - toctree_only = False - if len(entry) > 5: - toctree_only = entry[5] - destination = FileOutput( - destination_path=path.join(self.outdir, targetname), - encoding='utf-8') - self.info("processing " + targetname + "... ", nonl=1) - doctree = self.assemble_doctree(docname, toctree_only, - appendices=((docclass == 'manual') and - self.config.latex_appendices or [])) - self.post_process_images(doctree) - self.info("writing... ", nonl=1) - doctree.settings = docsettings - doctree.settings.author = author - doctree.settings.title = title - doctree.settings.docname = docname - doctree.settings.docclass = docclass - docwriter.write(doctree, destination) - self.info("done") - - def assemble_doctree(self, indexfile, toctree_only, appendices): - self.docnames = set([indexfile] + appendices) - self.info(darkgreen(indexfile) + " ", nonl=1) - def process_tree(docname, tree): - tree = tree.deepcopy() - for toctreenode in tree.traverse(addnodes.toctree): - newnodes = [] - includefiles = map(str, toctreenode['includefiles']) - for includefile in includefiles: - try: - self.info(darkgreen(includefile) + " ", nonl=1) - subtree = process_tree(includefile, - self.env.get_doctree(includefile)) - self.docnames.add(includefile) - except Exception: - self.warn('%s: toctree contains ref to nonexisting file %r' % - (docname, includefile)) - else: - sof = addnodes.start_of_file(file=includefile) - sof.children = subtree.children - newnodes.append(sof) - toctreenode.parent.replace(toctreenode, newnodes) - return tree - tree = self.env.get_doctree(indexfile) - if toctree_only: - # extract toctree nodes from the tree and put them in a fresh document - new_tree = new_document('<latex output>') - new_sect = nodes.section() - new_sect += nodes.title(u'<Set title in conf.py>', u'<Set title in conf.py>') - new_tree += new_sect - for node in tree.traverse(addnodes.toctree): - new_sect += node - tree = new_tree - largetree = process_tree(indexfile, tree) - largetree['file'] = indexfile - for docname in appendices: - appendix = self.env.get_doctree(docname) - appendix['file'] = docname - largetree.append(appendix) - self.info() - self.info("resolving references...") - self.env.resolve_references(largetree, indexfile, self) - # resolve :ref:s to distant tex files -- we can't add a cross-reference, - # but append the document name - for pendingnode in largetree.traverse(addnodes.pending_xref): - docname = pendingnode['refdocname'] - sectname = pendingnode['refsectname'] - newnodes = [nodes.emphasis(sectname, sectname)] - for subdir, title in self.titles: - if docname.startswith(subdir): - newnodes.append(nodes.Text(_(' (in '), _(' (in '))) - newnodes.append(nodes.emphasis(title, title)) - newnodes.append(nodes.Text(')', ')')) - break - else: - pass - pendingnode.replace_self(newnodes) - return largetree - - def finish(self): - # copy image files - if self.images: - self.info(bold('copying images...'), nonl=1) - for src, dest in self.images.iteritems(): - self.info(' '+src, nonl=1) - shutil.copyfile(path.join(self.srcdir, src), - path.join(self.outdir, dest)) - self.info() - - # the logo is handled differently - if self.config.latex_logo: - logobase = path.basename(self.config.latex_logo) - shutil.copyfile(path.join(self.confdir, self.config.latex_logo), - path.join(self.outdir, logobase)) - - self.info(bold('copying TeX support files... '), nonl=True) - staticdirname = path.join(path.dirname(__file__), 'texinputs') - for filename in os.listdir(staticdirname): - if not filename.startswith('.'): - shutil.copyfile(path.join(staticdirname, filename), - path.join(self.outdir, filename)) - self.info('done') - - -class ChangesBuilder(Builder): - """ - Write a summary with all versionadded/changed directives. - """ - name = 'changes' - - def init(self): - self.init_templates() - - def get_outdated_docs(self): - return self.outdir - - typemap = { - 'versionadded': 'added', - 'versionchanged': 'changed', - 'deprecated': 'deprecated', - } - - def write(self, *ignored): - version = self.config.version - libchanges = {} - apichanges = [] - otherchanges = {} - if version not in self.env.versionchanges: - self.info(bold('no changes in this version.')) - return - self.info(bold('writing summary file...')) - for type, docname, lineno, module, descname, content in \ - self.env.versionchanges[version]: - ttext = self.typemap[type] - context = content.replace('\n', ' ') - if descname and docname.startswith('c-api'): - if not descname: - continue - if context: - entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context) - else: - entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) - apichanges.append((entry, docname, lineno)) - elif descname or module: - if not module: - module = _('Builtins') - if not descname: - descname = _('Module level') - if context: - entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context) - else: - entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) - libchanges.setdefault(module, []).append((entry, docname, lineno)) - else: - if not context: - continue - entry = '<i>%s:</i> %s' % (ttext.capitalize(), context) - title = self.env.titles[docname].astext() - otherchanges.setdefault((docname, title), []).append( - (entry, docname, lineno)) - - ctx = { - 'project': self.config.project, - 'version': version, - 'docstitle': self.config.html_title, - 'shorttitle': self.config.html_short_title, - 'libchanges': sorted(libchanges.iteritems()), - 'apichanges': sorted(apichanges), - 'otherchanges': sorted(otherchanges.iteritems()), - 'show_sphinx': self.config.html_show_sphinx, - } - f = open(path.join(self.outdir, 'index.html'), 'w') - try: - f.write(self.templates.render('changes/frameset.html', ctx)) - finally: - f.close() - f = open(path.join(self.outdir, 'changes.html'), 'w') - try: - f.write(self.templates.render('changes/versionchanges.html', ctx)) - finally: - f.close() - - hltext = ['.. versionadded:: %s' % version, - '.. versionchanged:: %s' % version, - '.. deprecated:: %s' % version] - - def hl(no, line): - line = '<a name="L%s"> </a>' % no + escape(line) - for x in hltext: - if x in line: - line = '<span class="hl">%s</span>' % line - break - return line - - self.info(bold('copying source files...')) - for docname in self.env.all_docs: - f = open(self.env.doc2path(docname)) - lines = f.readlines() - targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html' - ensuredir(path.dirname(targetfn)) - f = codecs.open(targetfn, 'w', 'utf8') - try: - text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines)) - ctx = {'filename': self.env.doc2path(docname, None), 'text': text} - f.write(self.templates.render('changes/rstsource.html', ctx)) - finally: - f.close() - shutil.copyfile(path.join(path.dirname(__file__), 'static', 'default.css'), - path.join(self.outdir, 'default.css')) - - def hl(self, text, version): - text = escape(text) - for directive in ['versionchanged', 'versionadded', 'deprecated']: - text = text.replace('.. %s:: %s' % (directive, version), - '<b>.. %s:: %s</b>' % (directive, version)) - return text - - def finish(self): - pass - - -class TextBuilder(Builder): - name = 'text' - out_suffix = '.txt' - - def init(self): - pass - - def get_outdated_docs(self): - for docname in self.env.found_docs: - if docname not in self.env.all_docs: - yield docname - continue - targetname = self.env.doc2path(docname, self.outdir, self.out_suffix) - try: - targetmtime = path.getmtime(targetname) - except Exception: - targetmtime = 0 - try: - srcmtime = path.getmtime(self.env.doc2path(docname)) - if srcmtime > targetmtime: - yield docname - except EnvironmentError: - # source doesn't exist anymore - pass - - def get_target_uri(self, docname, typ=None): - return '' - - def prepare_writing(self, docnames): - self.writer = TextWriter(self) - - def write_doc(self, docname, doctree): - destination = StringOutput(encoding='utf-8') - self.writer.write(doctree, destination) - outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix) - ensuredir(path.dirname(outfilename)) # normally different from self.outdir - try: - f = codecs.open(outfilename, 'w', 'utf-8') - try: - f.write(self.writer.output) - finally: - f.close() - except (IOError, OSError), err: - self.warn("Error writing file %s: %s" % (outfilename, err)) - - def finish(self): - pass - - -# compatibility alias -WebHTMLBuilder = PickleHTMLBuilder - +import warnings -from sphinx.linkcheck import CheckExternalLinksBuilder +from sphinx.builders import Builder +from sphinx.builders.text import TextBuilder +from sphinx.builders.html import StandaloneHTMLBuilder, WebHTMLBuilder, \ + PickleHTMLBuilder, JSONHTMLBuilder +from sphinx.builders.latex import LaTeXBuilder +from sphinx.builders.changes import ChangesBuilder +from sphinx.builders.htmlhelp import HTMLHelpBuilder +from sphinx.builders.linkcheck import CheckExternalLinksBuilder -builtin_builders = { - 'html': StandaloneHTMLBuilder, - 'pickle': PickleHTMLBuilder, - 'json': JSONHTMLBuilder, - 'web': PickleHTMLBuilder, - 'htmlhelp': HTMLHelpBuilder, - 'latex': LaTeXBuilder, - 'text': TextBuilder, - 'changes': ChangesBuilder, - 'linkcheck': CheckExternalLinksBuilder, -} +warnings.warn('The sphinx.builder module is deprecated; please import ' + 'builders from the respective sphinx.builders submodules.', + DeprecationWarning, stacklevel=2) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py new file mode 100644 index 00000000..41f63de4 --- /dev/null +++ b/sphinx/builders/__init__.py @@ -0,0 +1,384 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders + ~~~~~~~~~~~~~~~ + + Builder superclass for all builders. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import gettext +from os import path + +from docutils import nodes + +from sphinx import package_dir, locale +from sphinx.util import SEP, relative_uri +from sphinx.environment import BuildEnvironment +from sphinx.util.console import bold, purple, darkgreen, term_width_line + +# side effect: registers roles and directives +from sphinx import roles +from sphinx import directives + + +ENV_PICKLE_FILENAME = 'environment.pickle' + + +class Builder(object): + """ + Builds target formats from the reST sources. + """ + + # builder's name, for the -b command line options + name = '' + # builder's output format, or '' if no document output is produced + format = '' + + def __init__(self, app, env=None, freshenv=False): + self.srcdir = app.srcdir + self.confdir = app.confdir + self.outdir = app.outdir + self.doctreedir = app.doctreedir + if not path.isdir(self.doctreedir): + os.makedirs(self.doctreedir) + + self.app = app + self.warn = app.warn + self.info = app.info + self.config = app.config + + self.load_i18n() + + # images that need to be copied over (source -> dest) + self.images = {} + + # if None, this is set in load_env() + self.env = env + self.freshenv = freshenv + + self.init() + self.load_env() + + # helper methods + + def init(self): + """ + Load necessary templates and perform initialization. The default + implementation does nothing. + """ + pass + + def create_template_bridge(self): + """ + Return the template bridge configured. + """ + if self.config.template_bridge: + self.templates = self.app.import_object( + self.config.template_bridge, 'template_bridge setting')() + else: + from sphinx.jinja2glue import BuiltinTemplateLoader + self.templates = BuiltinTemplateLoader() + + def get_target_uri(self, docname, typ=None): + """ + Return the target URI for a document name (*typ* can be used to qualify + the link characteristic for individual builders). + """ + raise NotImplementedError + + def get_relative_uri(self, from_, to, typ=None): + """ + Return a relative URI between two source filenames. May raise + environment.NoUri if there's no way to return a sensible URI. + """ + return relative_uri(self.get_target_uri(from_), + self.get_target_uri(to, typ)) + + def get_outdated_docs(self): + """ + Return an iterable of output files that are outdated, or a string + describing what an update build will build. + + If the builder does not output individual files corresponding to + source files, return a string here. If it does, return an iterable + of those files that need to be written. + """ + raise NotImplementedError + + def old_status_iterator(self, iterable, summary, colorfunc=darkgreen): + l = 0 + for item in iterable: + if l == 0: + self.info(bold(summary), nonl=1) + l = 1 + self.info(colorfunc(item) + ' ', nonl=1) + yield item + if l == 1: + self.info() + + # new version with progress info + def status_iterator(self, iterable, summary, colorfunc=darkgreen, length=0): + if length == 0: + for item in self.old_status_iterator(iterable, summary, colorfunc): + yield item + return + l = 0 + summary = bold(summary) + for item in iterable: + l += 1 + self.info(term_width_line('%s[%3d%%] %s' % + (summary, 100*l/length, + colorfunc(item))), nonl=1) + yield item + if l > 0: + self.info() + + supported_image_types = [] + + def post_process_images(self, doctree): + """ + Pick the best candidate for all image URIs. + """ + for node in doctree.traverse(nodes.image): + if '?' in node['candidates']: + # don't rewrite nonlocal image URIs + continue + if '*' not in node['candidates']: + for imgtype in self.supported_image_types: + candidate = node['candidates'].get(imgtype, None) + if candidate: + break + else: + self.warn( + 'no matching candidate for image URI %r' % node['uri'], + '%s:%s' % (node.source, getattr(node, 'line', ''))) + continue + node['uri'] = candidate + else: + candidate = node['uri'] + if candidate not in self.env.images: + # non-existing URI; let it alone + continue + self.images[candidate] = self.env.images[candidate][1] + + # build methods + + def load_i18n(self): + """ + Load translated strings from the configured localedirs if + enabled in the configuration. + """ + self.translator = None + if self.config.language is not None: + self.info(bold('loading translations [%s]... ' % + self.config.language), nonl=True) + locale_dirs = [path.join(package_dir, 'locale')] + \ + [path.join(self.srcdir, x) for x in self.config.locale_dirs] + for dir_ in locale_dirs: + try: + trans = gettext.translation('sphinx', localedir=dir_, + languages=[self.config.language]) + if self.translator is None: + self.translator = trans + else: + self.translator._catalog.update(trans.catalog) + except Exception: + # Language couldn't be found in the specified path + pass + if self.translator is not None: + self.info('done') + else: + self.info('locale not available') + if self.translator is None: + self.translator = gettext.NullTranslations() + self.translator.install(unicode=True) + locale.init() # translate common labels + + def load_env(self): + """Set up the build environment.""" + if self.env: + return + if not self.freshenv: + try: + self.info(bold('loading pickled environment... '), nonl=True) + self.env = BuildEnvironment.frompickle(self.config, + path.join(self.doctreedir, ENV_PICKLE_FILENAME)) + self.info('done') + except Exception, err: + if type(err) is IOError and err.errno == 2: + self.info('not found') + else: + self.info('failed: %s' % err) + self.env = BuildEnvironment(self.srcdir, self.doctreedir, + self.config) + self.env.find_files(self.config) + else: + self.env = BuildEnvironment(self.srcdir, self.doctreedir, + self.config) + self.env.find_files(self.config) + self.env.set_warnfunc(self.warn) + + def build_all(self): + """Build all source files.""" + self.build(None, summary='all source files', method='all') + + def build_specific(self, filenames): + """Only rebuild as much as needed for changes in the *filenames*.""" + # bring the filenames to the canonical format, that is, + # relative to the source directory and without source_suffix. + dirlen = len(self.srcdir) + 1 + to_write = [] + suffix = self.config.source_suffix + for filename in filenames: + filename = path.abspath(filename)[dirlen:] + if filename.endswith(suffix): + filename = filename[:-len(suffix)] + filename = filename.replace(os.path.sep, SEP) + to_write.append(filename) + self.build(to_write, method='specific', + summary='%d source files given on command ' + 'line' % len(to_write)) + + def build_update(self): + """Only rebuild what was changed or added since last build.""" + to_build = self.get_outdated_docs() + if isinstance(to_build, str): + self.build(['__all__'], to_build) + else: + to_build = list(to_build) + self.build(to_build, + summary='targets for %d source files that are ' + 'out of date' % len(to_build)) + + def build(self, docnames, summary=None, method='update'): + """ + Main build method. First updates the environment, and then + calls :meth:`write`. + """ + if summary: + self.info(bold('building [%s]: ' % self.name), nonl=1) + self.info(summary) + + updated_docnames = set() + # while reading, collect all warnings from docutils + warnings = [] + self.env.set_warnfunc(lambda *args: warnings.append(args)) + self.info(bold('updating environment: '), nonl=1) + msg, length, iterator = self.env.update(self.config, self.srcdir, + self.doctreedir, self.app) + self.info(msg) + for docname in self.status_iterator(iterator, 'reading sources... ', + purple, length): + updated_docnames.add(docname) + # nothing further to do, the environment has already + # done the reading + for warning in warnings: + self.warn(*warning) + self.env.set_warnfunc(self.warn) + + doccount = len(updated_docnames) + self.info(bold('looking for now-outdated files... '), nonl=1) + for docname in self.env.check_dependents(updated_docnames): + updated_docnames.add(docname) + outdated = len(updated_docnames) - doccount + if outdated: + self.info('%d found' % outdated) + else: + self.info('none found') + + if updated_docnames: + # save the environment + self.info(bold('pickling environment... '), nonl=True) + self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME)) + self.info('done') + + # global actions + self.info(bold('checking consistency... '), nonl=True) + self.env.check_consistency() + self.info('done') + else: + if method == 'update' and not docnames: + self.info(bold('no targets are out of date.')) + return + + # another indirection to support builders that don't build + # files individually + self.write(docnames, list(updated_docnames), method) + + # finish (write static files etc.) + self.finish() + status = (self.app.statuscode == 0 and 'succeeded' + or 'finished with problems') + if self.app._warncount: + self.info(bold('build %s, %s warning%s.' % + (status, self.app._warncount, + self.app._warncount != 1 and 's' or ''))) + else: + self.info(bold('build %s.' % status)) + + def write(self, build_docnames, updated_docnames, method='update'): + if build_docnames is None or build_docnames == ['__all__']: + # build_all + build_docnames = self.env.found_docs + if method == 'update': + # build updated ones as well + docnames = set(build_docnames) | set(updated_docnames) + else: + docnames = set(build_docnames) + + # add all toctree-containing files that may have changed + for docname in list(docnames): + for tocdocname in self.env.files_to_rebuild.get(docname, []): + docnames.add(tocdocname) + docnames.add(self.config.master_doc) + + self.info(bold('preparing documents... '), nonl=True) + self.prepare_writing(docnames) + self.info('done') + + # write target files + warnings = [] + self.env.set_warnfunc(lambda *args: warnings.append(args)) + for docname in self.status_iterator( + sorted(docnames), 'writing output... ', darkgreen, len(docnames)): + doctree = self.env.get_and_resolve_doctree(docname, self) + self.write_doc(docname, doctree) + for warning in warnings: + self.warn(*warning) + self.env.set_warnfunc(self.warn) + + def prepare_writing(self, docnames): + raise NotImplementedError + + def write_doc(self, docname, doctree): + raise NotImplementedError + + def finish(self): + """ + Finish the building process. The default implementation does nothing. + """ + pass + + def cleanup(self): + """ + Cleanup any resources. The default implementation does nothing. + """ + + +BUILTIN_BUILDERS = { + 'html': ('html', 'StandaloneHTMLBuilder'), + 'dirhtml': ('html', 'DirectoryHTMLBuilder'), + 'pickle': ('html', 'PickleHTMLBuilder'), + 'json': ('html', 'JSONHTMLBuilder'), + 'web': ('html', 'PickleHTMLBuilder'), + 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'), + 'qthelp': ('qthelp', 'QtHelpBuilder'), + 'latex': ('latex', 'LaTeXBuilder'), + 'text': ('text', 'TextBuilder'), + 'changes': ('changes', 'ChangesBuilder'), + 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), +} diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py new file mode 100644 index 00000000..e07b06d8 --- /dev/null +++ b/sphinx/builders/changes.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.changes + ~~~~~~~~~~~~~~~~~~~~~~~ + + Changelog builder. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs +import shutil +from os import path +from cgi import escape + +from sphinx import package_dir +from sphinx.util import ensuredir, os_path, copy_static_entry +from sphinx.theming import Theme +from sphinx.builders import Builder +from sphinx.util.console import bold + + +class ChangesBuilder(Builder): + """ + Write a summary with all versionadded/changed directives. + """ + name = 'changes' + + def init(self): + self.create_template_bridge() + Theme.init_themes(self) + self.theme = Theme('default') + self.templates.init(self, self.theme) + + def get_outdated_docs(self): + return self.outdir + + typemap = { + 'versionadded': 'added', + 'versionchanged': 'changed', + 'deprecated': 'deprecated', + } + + def write(self, *ignored): + version = self.config.version + libchanges = {} + apichanges = [] + otherchanges = {} + if version not in self.env.versionchanges: + self.info(bold('no changes in version %s.' % version)) + return + self.info(bold('writing summary file...')) + for type, docname, lineno, module, descname, content in \ + self.env.versionchanges[version]: + if isinstance(descname, tuple): + descname = descname[0] + ttext = self.typemap[type] + context = content.replace('\n', ' ') + if descname and docname.startswith('c-api'): + if not descname: + continue + if context: + entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, + context) + else: + entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) + apichanges.append((entry, docname, lineno)) + elif descname or module: + if not module: + module = _('Builtins') + if not descname: + descname = _('Module level') + if context: + entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, + context) + else: + entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) + libchanges.setdefault(module, []).append((entry, docname, + lineno)) + else: + if not context: + continue + entry = '<i>%s:</i> %s' % (ttext.capitalize(), context) + title = self.env.titles[docname].astext() + otherchanges.setdefault((docname, title), []).append( + (entry, docname, lineno)) + + ctx = { + 'project': self.config.project, + 'version': version, + 'docstitle': self.config.html_title, + 'shorttitle': self.config.html_short_title, + 'libchanges': sorted(libchanges.iteritems()), + 'apichanges': sorted(apichanges), + 'otherchanges': sorted(otherchanges.iteritems()), + 'show_sphinx': self.config.html_show_sphinx, + } + f = codecs.open(path.join(self.outdir, 'index.html'), 'w', 'utf8') + try: + f.write(self.templates.render('changes/frameset.html', ctx)) + finally: + f.close() + f = codecs.open(path.join(self.outdir, 'changes.html'), 'w', 'utf8') + try: + f.write(self.templates.render('changes/versionchanges.html', ctx)) + finally: + f.close() + + hltext = ['.. versionadded:: %s' % version, + '.. versionchanged:: %s' % version, + '.. deprecated:: %s' % version] + + def hl(no, line): + line = '<a name="L%s"> </a>' % no + escape(line) + for x in hltext: + if x in line: + line = '<span class="hl">%s</span>' % line + break + return line + + self.info(bold('copying source files...')) + for docname in self.env.all_docs: + f = codecs.open(self.env.doc2path(docname), 'r', 'latin1') + lines = f.readlines() + targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html' + ensuredir(path.dirname(targetfn)) + f = codecs.open(targetfn, 'w', 'latin1') + try: + text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines)) + ctx = { + 'filename': self.env.doc2path(docname, None), + 'text': text + } + f.write(self.templates.render('changes/rstsource.html', ctx)) + finally: + f.close() + themectx = dict(('theme_' + key, val) for (key, val) in + self.theme.get_options({}).iteritems()) + copy_static_entry(path.join(package_dir, 'themes', 'default', + 'static', 'default.css_t'), + path.join(self.outdir, 'default.css_t'), + self, themectx) + copy_static_entry(path.join(package_dir, 'themes', 'basic', + 'static', 'basic.css'), + path.join(self.outdir, 'basic.css'), self) + + def hl(self, text, version): + text = escape(text) + for directive in ['versionchanged', 'versionadded', 'deprecated']: + text = text.replace('.. %s:: %s' % (directive, version), + '<b>.. %s:: %s</b>' % (directive, version)) + return text + + def finish(self): + pass diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py new file mode 100644 index 00000000..365cf5f9 --- /dev/null +++ b/sphinx/builders/html.py @@ -0,0 +1,836 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.html + ~~~~~~~~~~~~~~~~~~~~ + + Several HTML builders. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import codecs +import shutil +import posixpath +import cPickle as pickle +from os import path +try: + from hashlib import md5 +except ImportError: + # 2.4 compatibility + from md5 import md5 + +from docutils import nodes +from docutils.io import DocTreeInput, StringOutput +from docutils.core import publish_parts +from docutils.utils import new_document +from docutils.frontend import OptionParser +from docutils.readers.doctree import Reader as DoctreeReader + +from sphinx import package_dir, __version__ +from sphinx.util import SEP, os_path, relative_uri, ensuredir, \ + movefile, ustrftime, copy_static_entry +from sphinx.errors import SphinxError +from sphinx.search import js_index +from sphinx.theming import Theme +from sphinx.builders import Builder, ENV_PICKLE_FILENAME +from sphinx.highlighting import PygmentsBridge +from sphinx.util.console import bold +from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ + SmartyPantsHTMLTranslator + +try: + import json +except ImportError: + try: + import simplejson as json + except ImportError: + json = None + +#: the filename for the inventory of objects +INVENTORY_FILENAME = 'objects.inv' +#: the filename for the "last build" file (for serializing builders) +LAST_BUILD_FILENAME = 'last_build' + + +class StandaloneHTMLBuilder(Builder): + """ + Builds standalone HTML docs. + """ + name = 'html' + format = 'html' + copysource = True + out_suffix = '.html' + link_suffix = '.html' # defaults to matching out_suffix + indexer_format = js_index + supported_image_types = ['image/svg+xml', 'image/png', + 'image/gif', 'image/jpeg'] + searchindex_filename = 'searchindex.js' + add_permalinks = True + embedded = False # for things like HTML help or Qt help: suppresses sidebar + + # This is a class attribute because it is mutated by Sphinx.add_javascript. + script_files = ['_static/jquery.js', '_static/doctools.js'] + + def init(self): + # a hash of all config values that, if changed, cause a full rebuild + self.config_hash = '' + self.tags_hash = '' + # section numbers for headings in the currently visited document + self.secnumbers = {} + + self.init_templates() + self.init_highlighter() + self.init_translator_class() + if self.config.html_file_suffix: + self.out_suffix = self.config.html_file_suffix + + if self.config.html_link_suffix is not None: + self.link_suffix = self.config.html_link_suffix + else: + self.link_suffix = self.out_suffix + + if self.config.language is not None: + jsfile = path.join(package_dir, 'locale', self.config.language, + 'LC_MESSAGES', 'sphinx.js') + if path.isfile(jsfile): + self.script_files.append('_static/translations.js') + + def init_templates(self): + Theme.init_themes(self) + self.theme = Theme(self.config.html_theme) + self.create_template_bridge() + self.templates.init(self, self.theme) + + def init_highlighter(self): + # determine Pygments style and create the highlighter + if self.config.pygments_style is not None: + style = self.config.pygments_style + elif self.theme: + style = self.theme.get_confstr('theme', 'pygments_style', 'none') + else: + style = 'sphinx' + self.highlighter = PygmentsBridge('html', style) + + def init_translator_class(self): + if self.config.html_translator_class: + self.translator_class = self.app.import_object( + self.config.html_translator_class, + 'html_translator_class setting') + elif self.config.html_use_smartypants: + self.translator_class = SmartyPantsHTMLTranslator + else: + self.translator_class = HTMLTranslator + + def get_outdated_docs(self): + cfgdict = dict((name, self.config[name]) + for (name, desc) in self.config.values.iteritems() + if desc[1] == 'html') + self.config_hash = md5(str(cfgdict)).hexdigest() + self.tags_hash = md5(str(sorted(self.tags))).hexdigest() + old_config_hash = old_tags_hash = '' + try: + fp = open(path.join(self.outdir, '.buildinfo')) + version = fp.readline() + if version.rstrip() != '# Sphinx build info version 1': + raise ValueError + fp.readline() # skip commentary + cfg, old_config_hash = fp.readline().strip().split(': ') + if cfg != 'config': + raise ValueError + tag, old_tags_hash = fp.readline().strip().split(': ') + if tag != 'tags': + raise ValueError + fp.close() + except ValueError: + self.warn('unsupported build info format in %r, building all' % + path.join(self.outdir, '.buildinfo')) + except Exception: + pass + if old_config_hash != self.config_hash or \ + old_tags_hash != self.tags_hash: + for docname in self.env.found_docs: + yield docname + return + + if self.templates: + template_mtime = self.templates.newest_template_mtime() + else: + template_mtime = 0 + for docname in self.env.found_docs: + if docname not in self.env.all_docs: + yield docname + continue + targetname = self.env.doc2path(docname, self.outdir, + self.out_suffix) + try: + targetmtime = path.getmtime(targetname) + except Exception: + targetmtime = 0 + try: + srcmtime = max(path.getmtime(self.env.doc2path(docname)), + template_mtime) + if srcmtime > targetmtime: + yield docname + except EnvironmentError: + # source doesn't exist anymore + pass + + def render_partial(self, node): + """Utility: Render a lone doctree node.""" + doc = new_document('<partial node>') + doc.append(node) + return publish_parts( + doc, + source_class=DocTreeInput, + reader=DoctreeReader(), + writer=HTMLWriter(self), + settings_overrides={'output_encoding': 'unicode'} + ) + + def prepare_writing(self, docnames): + from sphinx.search import IndexBuilder + + self.indexer = IndexBuilder(self.env) + self.load_indexer(docnames) + self.docwriter = HTMLWriter(self) + self.docsettings = OptionParser( + defaults=self.env.settings, + components=(self.docwriter,)).get_default_values() + + # format the "last updated on" string, only once is enough since it + # typically doesn't include the time of day + lufmt = self.config.html_last_updated_fmt + if lufmt is not None: + self.last_updated = ustrftime(lufmt or _('%b %d, %Y')) + else: + self.last_updated = None + + logo = self.config.html_logo and \ + path.basename(self.config.html_logo) or '' + + favicon = self.config.html_favicon and \ + path.basename(self.config.html_favicon) or '' + if favicon and os.path.splitext(favicon)[1] != '.ico': + self.warn('html_favicon is not an .ico file') + + if not isinstance(self.config.html_use_opensearch, basestring): + self.warn('html_use_opensearch config value must now be a string') + + self.relations = self.env.collect_relations() + + rellinks = [] + if self.config.html_use_index: + rellinks.append(('genindex', _('General Index'), 'I', _('index'))) + if self.config.html_use_modindex and self.env.modules: + rellinks.append(('modindex', _('Global Module Index'), + 'M', _('modules'))) + + if self.config.html_style is not None: + stylename = self.config.html_style + elif self.theme: + stylename = self.theme.get_confstr('theme', 'stylesheet') + else: + stylename = 'default.css' + + self.globalcontext = dict( + embedded = self.embedded, + project = self.config.project, + release = self.config.release, + version = self.config.version, + last_updated = self.last_updated, + copyright = self.config.copyright, + master_doc = self.config.master_doc, + use_opensearch = self.config.html_use_opensearch, + docstitle = self.config.html_title, + shorttitle = self.config.html_short_title, + show_sphinx = self.config.html_show_sphinx, + has_source = self.config.html_copy_source, + show_source = self.config.html_show_sourcelink, + file_suffix = self.out_suffix, + script_files = self.script_files, + sphinx_version = __version__, + style = stylename, + rellinks = rellinks, + builder = self.name, + parents = [], + logo = logo, + favicon = favicon, + ) + if self.theme: + self.globalcontext.update( + ('theme_' + key, val) for (key, val) in + self.theme.get_options( + self.config.html_theme_options).iteritems()) + self.globalcontext.update(self.config.html_context) + + def get_doc_context(self, docname, body, metatags): + """Collect items for the template context of a page.""" + # find out relations + prev = next = None + parents = [] + rellinks = self.globalcontext['rellinks'][:] + related = self.relations.get(docname) + titles = self.env.titles + if related and related[2]: + try: + next = { + 'link': self.get_relative_uri(docname, related[2]), + 'title': self.render_partial(titles[related[2]])['title'] + } + rellinks.append((related[2], next['title'], 'N', _('next'))) + except KeyError: + next = None + if related and related[1]: + try: + prev = { + 'link': self.get_relative_uri(docname, related[1]), + 'title': self.render_partial(titles[related[1]])['title'] + } + rellinks.append((related[1], prev['title'], 'P', _('previous'))) + except KeyError: + # the relation is (somehow) not in the TOC tree, handle + # that gracefully + prev = None + while related and related[0]: + try: + parents.append( + {'link': self.get_relative_uri(docname, related[0]), + 'title': self.render_partial(titles[related[0]])['title']}) + except KeyError: + pass + related = self.relations.get(related[0]) + if parents: + parents.pop() # remove link to the master file; we have a generic + # "back to index" link already + parents.reverse() + + # title rendered as HTML + title = titles.get(docname) + title = title and self.render_partial(title)['title'] or '' + # the name for the copied source + sourcename = self.config.html_copy_source and docname + '.txt' or '' + + # metadata for the document + meta = self.env.metadata.get(docname) + + # local TOC and global TOC tree + toc = self.render_partial(self.env.get_toc_for(docname))['fragment'] + + return dict( + parents = parents, + prev = prev, + next = next, + title = title, + meta = meta, + body = body, + metatags = metatags, + rellinks = rellinks, + sourcename = sourcename, + toc = toc, + # only display a TOC if there's more than one item to show + display_toc = (self.env.toc_num_entries[docname] > 1), + ) + + def write_doc(self, docname, doctree): + destination = StringOutput(encoding='utf-8') + doctree.settings = self.docsettings + + self.secnumbers = self.env.toc_secnumbers.get(docname, {}) + self.imgpath = relative_uri(self.get_target_uri(docname), '_images') + self.post_process_images(doctree) + self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads') + self.docwriter.write(doctree, destination) + self.docwriter.assemble_parts() + body = self.docwriter.parts['fragment'] + metatags = self.docwriter.clean_meta + + ctx = self.get_doc_context(docname, body, metatags) + self.index_page(docname, doctree, ctx.get('title', '')) + self.handle_page(docname, ctx, event_arg=doctree) + + def finish(self): + self.info(bold('writing additional files...'), nonl=1) + + # the global general index + + if self.config.html_use_index: + # the total count of lines for each index letter, used to distribute + # the entries into two columns + genindex = self.env.create_index(self) + indexcounts = [] + for _, entries in genindex: + indexcounts.append(sum(1 + len(subitems) + for _, (_, subitems) in entries)) + + genindexcontext = dict( + genindexentries = genindex, + genindexcounts = indexcounts, + split_index = self.config.html_split_index, + ) + self.info(' genindex', nonl=1) + + if self.config.html_split_index: + self.handle_page('genindex', genindexcontext, + 'genindex-split.html') + self.handle_page('genindex-all', genindexcontext, + 'genindex.html') + for (key, entries), count in zip(genindex, indexcounts): + ctx = {'key': key, 'entries': entries, 'count': count, + 'genindexentries': genindex} + self.handle_page('genindex-' + key, ctx, + 'genindex-single.html') + else: + self.handle_page('genindex', genindexcontext, 'genindex.html') + + # the global module index + + if self.config.html_use_modindex and self.env.modules: + # the sorted list of all modules, for the global module index + modules = sorted(((mn, (self.get_relative_uri('modindex', fn) + + '#module-' + mn, sy, pl, dep)) + for (mn, (fn, sy, pl, dep)) in + self.env.modules.iteritems()), + key=lambda x: x[0].lower()) + # collect all platforms + platforms = set() + # sort out collapsable modules + modindexentries = [] + letters = [] + pmn = '' + num_toplevels = 0 + num_collapsables = 0 + cg = 0 # collapse group + fl = '' # first letter + for mn, (fn, sy, pl, dep) in modules: + pl = pl and pl.split(', ') or [] + platforms.update(pl) + + ignore = self.env.config['modindex_common_prefix'] + ignore = sorted(ignore, key=len, reverse=True) + for i in ignore: + if mn.startswith(i): + mn = mn[len(i):] + stripped = i + break + else: + stripped = '' + + if fl != mn[0].lower() and mn[0] != '_': + # heading + letter = mn[0].upper() + if letter not in letters: + modindexentries.append(['', False, 0, False, + letter, '', [], False, '']) + letters.append(letter) + tn = mn.split('.')[0] + if tn != mn: + # submodule + if pmn == tn: + # first submodule - make parent collapsable + modindexentries[-1][1] = True + num_collapsables += 1 + elif not pmn.startswith(tn): + # submodule without parent in list, add dummy entry + cg += 1 + modindexentries.append([tn, True, cg, False, '', '', + [], False, stripped]) + else: + num_toplevels += 1 + cg += 1 + modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, + dep, stripped]) + pmn = mn + fl = mn[0].lower() + platforms = sorted(platforms) + + # apply heuristics when to collapse modindex at page load: + # only collapse if number of toplevel modules is larger than + # number of submodules + collapse = len(modules) - num_toplevels < num_toplevels + + # As some parts of the module names may have been stripped, those + # names have changed, thus it is necessary to sort the entries. + if ignore: + def sorthelper(entry): + name = entry[0] + if name == '': + # heading + name = entry[4] + return name.lower() + + modindexentries.sort(key=sorthelper) + letters.sort() + + modindexcontext = dict( + modindexentries = modindexentries, + platforms = platforms, + letters = letters, + collapse_modindex = collapse, + ) + self.info(' modindex', nonl=1) + self.handle_page('modindex', modindexcontext, 'modindex.html') + + # the search page + if self.name != 'htmlhelp': + self.info(' search', nonl=1) + self.handle_page('search', {}, 'search.html') + + # additional pages from conf.py + for pagename, template in self.config.html_additional_pages.items(): + self.info(' '+pagename, nonl=1) + self.handle_page(pagename, {}, template) + + if self.config.html_use_opensearch and self.name != 'htmlhelp': + self.info(' opensearch', nonl=1) + fn = path.join(self.outdir, '_static', 'opensearch.xml') + self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn) + + self.info() + + # copy image files + if self.images: + self.info(bold('copying images...'), nonl=True) + ensuredir(path.join(self.outdir, '_images')) + for src, dest in self.images.iteritems(): + self.info(' '+src, nonl=1) + shutil.copyfile(path.join(self.srcdir, src), + path.join(self.outdir, '_images', dest)) + self.info() + + # copy downloadable files + if self.env.dlfiles: + self.info(bold('copying downloadable files...'), nonl=True) + ensuredir(path.join(self.outdir, '_downloads')) + for src, (_, dest) in self.env.dlfiles.iteritems(): + self.info(' '+src, nonl=1) + shutil.copyfile(path.join(self.srcdir, src), + path.join(self.outdir, '_downloads', dest)) + self.info() + + # copy static files + self.info(bold('copying static files... '), nonl=True) + ensuredir(path.join(self.outdir, '_static')) + # first, create pygments style file + f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w') + f.write(self.highlighter.get_stylesheet()) + f.close() + # then, copy translations JavaScript file + if self.config.language is not None: + jsfile = path.join(package_dir, 'locale', self.config.language, + 'LC_MESSAGES', 'sphinx.js') + if path.isfile(jsfile): + shutil.copyfile(jsfile, path.join(self.outdir, '_static', + 'translations.js')) + # then, copy over all user-supplied static files + if self.theme: + staticdirnames = [path.join(themepath, 'static') + for themepath in self.theme.get_dirchain()[::-1]] + else: + staticdirnames = [] + staticdirnames += [path.join(self.confdir, spath) + for spath in self.config.html_static_path] + for staticdirname in staticdirnames: + if not path.isdir(staticdirname): + self.warn('static directory %r does not exist' % staticdirname) + continue + for filename in os.listdir(staticdirname): + if filename.startswith('.'): + continue + fullname = path.join(staticdirname, filename) + targetname = path.join(self.outdir, '_static', filename) + copy_static_entry(fullname, targetname, self, + self.globalcontext) + # last, copy logo file (handled differently) + if self.config.html_logo: + logobase = path.basename(self.config.html_logo) + shutil.copyfile(path.join(self.confdir, self.config.html_logo), + path.join(self.outdir, '_static', logobase)) + + # write build info file + fp = open(path.join(self.outdir, '.buildinfo'), 'w') + try: + fp.write('# Sphinx build info version 1\n' + '# This file hashes the configuration used when building' + ' these files. When it is not found, a full rebuild will' + ' be done.\nconfig: %s\ntags: %s\n' % + (self.config_hash, self.tags_hash)) + finally: + fp.close() + + self.info('done') + + # dump the search index + self.handle_finish() + + def cleanup(self): + # clean up theme stuff + if self.theme: + self.theme.cleanup() + + def post_process_images(self, doctree): + """ + Pick the best candiate for an image and link down-scaled images to + their high res version. + """ + Builder.post_process_images(self, doctree) + for node in doctree.traverse(nodes.image): + if not node.has_key('scale') or \ + isinstance(node.parent, nodes.reference): + # docutils does unfortunately not preserve the + # ``target`` attribute on images, so we need to check + # the parent node here. + continue + uri = node['uri'] + reference = nodes.reference() + if uri in self.images: + reference['refuri'] = posixpath.join(self.imgpath, + self.images[uri]) + else: + reference['refuri'] = uri + node.replace_self(reference) + reference.append(node) + + def load_indexer(self, docnames): + keep = set(self.env.all_docs) - set(docnames) + try: + f = open(path.join(self.outdir, self.searchindex_filename), 'rb') + try: + self.indexer.load(f, self.indexer_format) + finally: + f.close() + except (IOError, OSError, ValueError): + if keep: + self.warn('search index couldn\'t be loaded, but not all ' + 'documents will be built: the index will be ' + 'incomplete.') + # delete all entries for files that will be rebuilt + self.indexer.prune(keep) + + def index_page(self, pagename, doctree, title): + # only index pages with title + if self.indexer is not None and title: + self.indexer.feed(pagename, title, doctree) + + def _get_local_toctree(self, docname, collapse=True): + return self.render_partial(self.env.get_toctree_for( + docname, self, collapse))['fragment'] + + def get_outfilename(self, pagename): + return path.join(self.outdir, os_path(pagename) + self.out_suffix) + + # --------- these are overwritten by the serialization builder + + def get_target_uri(self, docname, typ=None): + return docname + self.link_suffix + + def handle_page(self, pagename, addctx, templatename='page.html', + outfilename=None, event_arg=None): + ctx = self.globalcontext.copy() + # current_page_name is backwards compatibility + ctx['pagename'] = ctx['current_page_name'] = pagename + + def pathto(otheruri, resource=False, + baseuri=self.get_target_uri(pagename)): + if not resource: + otheruri = self.get_target_uri(otheruri) + return relative_uri(baseuri, otheruri) + ctx['pathto'] = pathto + ctx['hasdoc'] = lambda name: name in self.env.all_docs + ctx['customsidebar'] = self.config.html_sidebars.get(pagename) + ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw) + ctx.update(addctx) + + self.app.emit('html-page-context', pagename, templatename, + ctx, event_arg) + + output = self.templates.render(templatename, ctx) + if not outfilename: + outfilename = self.get_outfilename(pagename) + # outfilename's path is in general different from self.outdir + ensuredir(path.dirname(outfilename)) + try: + f = codecs.open(outfilename, 'w', 'utf-8') + try: + f.write(output) + finally: + f.close() + except (IOError, OSError), err: + self.warn("error writing file %s: %s" % (outfilename, err)) + if self.copysource and ctx.get('sourcename'): + # copy the source file for the "show source" link + source_name = path.join(self.outdir, '_sources', + os_path(ctx['sourcename'])) + ensuredir(path.dirname(source_name)) + shutil.copyfile(self.env.doc2path(pagename), source_name) + + def handle_finish(self): + self.info(bold('dumping search index... '), nonl=True) + self.indexer.prune(self.env.all_docs) + searchindexfn = path.join(self.outdir, self.searchindex_filename) + # first write to a temporary file, so that if dumping fails, + # the existing index won't be overwritten + f = open(searchindexfn + '.tmp', 'wb') + try: + self.indexer.dump(f, self.indexer_format) + finally: + f.close() + movefile(searchindexfn + '.tmp', searchindexfn) + self.info('done') + + self.info(bold('dumping object inventory... '), nonl=True) + f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') + try: + f.write('# Sphinx inventory version 1\n') + f.write('# Project: %s\n' % self.config.project.encode('utf-8')) + f.write('# Version: %s\n' % self.config.version) + for modname, info in self.env.modules.iteritems(): + f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) + for refname, (docname, desctype) in self.env.descrefs.iteritems(): + f.write('%s %s %s\n' % (refname, desctype, + self.get_target_uri(docname))) + finally: + f.close() + self.info('done') + + +class DirectoryHTMLBuilder(StandaloneHTMLBuilder): + """ + A StandaloneHTMLBuilder that creates all HTML pages as "index.html" in + a directory given by their pagename, so that generated URLs don't have + ``.html`` in them. + """ + name = 'dirhtml' + + def get_target_uri(self, docname, typ=None): + if docname == 'index': + return '' + if docname.endswith(SEP + 'index'): + return docname[:-5] # up to sep + return docname + SEP + + def get_outfilename(self, pagename): + if pagename == 'index' or pagename.endswith(SEP + 'index'): + outfilename = path.join(self.outdir, os_path(pagename) + + self.out_suffix) + else: + outfilename = path.join(self.outdir, os_path(pagename), + 'index' + self.out_suffix) + + return outfilename + + +class SerializingHTMLBuilder(StandaloneHTMLBuilder): + """ + An abstract builder that serializes the generated HTML. + """ + #: the serializing implementation to use. Set this to a module that + #: implements a `dump`, `load`, `dumps` and `loads` functions + #: (pickle, simplejson etc.) + implementation = None + + #: the filename for the global context file + globalcontext_filename = None + + supported_image_types = ['image/svg+xml', 'image/png', + 'image/gif', 'image/jpeg'] + + def init(self): + self.config_hash = '' + self.tags_hash = '' + self.theme = None # no theme necessary + self.templates = None # no template bridge necessary + self.init_translator_class() + self.init_highlighter() + + def get_target_uri(self, docname, typ=None): + if docname == 'index': + return '' + if docname.endswith(SEP + 'index'): + return docname[:-5] # up to sep + return docname + SEP + + def handle_page(self, pagename, ctx, templatename='page.html', + outfilename=None, event_arg=None): + ctx['current_page_name'] = pagename + sidebarfile = self.config.html_sidebars.get(pagename) + if sidebarfile: + ctx['customsidebar'] = sidebarfile + + if not outfilename: + outfilename = path.join(self.outdir, + os_path(pagename) + self.out_suffix) + + self.app.emit('html-page-context', pagename, templatename, + ctx, event_arg) + + ensuredir(path.dirname(outfilename)) + f = open(outfilename, 'wb') + try: + self.implementation.dump(ctx, f, 2) + finally: + f.close() + + # if there is a source file, copy the source file for the + # "show source" link + if ctx.get('sourcename'): + source_name = path.join(self.outdir, '_sources', + os_path(ctx['sourcename'])) + ensuredir(path.dirname(source_name)) + shutil.copyfile(self.env.doc2path(pagename), source_name) + + def handle_finish(self): + # dump the global context + outfilename = path.join(self.outdir, self.globalcontext_filename) + f = open(outfilename, 'wb') + try: + self.implementation.dump(self.globalcontext, f, 2) + finally: + f.close() + + # super here to dump the search index + StandaloneHTMLBuilder.handle_finish(self) + + # copy the environment file from the doctree dir to the output dir + # as needed by the web app + shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME), + path.join(self.outdir, ENV_PICKLE_FILENAME)) + + # touch 'last build' file, used by the web application to determine + # when to reload its environment and clear the cache + open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close() + + +class PickleHTMLBuilder(SerializingHTMLBuilder): + """ + A Builder that dumps the generated HTML into pickle files. + """ + implementation = pickle + indexer_format = pickle + name = 'pickle' + out_suffix = '.fpickle' + globalcontext_filename = 'globalcontext.pickle' + searchindex_filename = 'searchindex.pickle' + +# compatibility alias +WebHTMLBuilder = PickleHTMLBuilder + + +class JSONHTMLBuilder(SerializingHTMLBuilder): + """ + A builder that dumps the generated HTML into JSON files. + """ + implementation = json + indexer_format = json + name = 'json' + out_suffix = '.fjson' + globalcontext_filename = 'globalcontext.json' + searchindex_filename = 'searchindex.json' + + def init(self): + if json is None: + raise SphinxError( + 'The module simplejson (or json in Python >= 2.6) ' + 'is not available. The JSONHTMLBuilder builder will not work.') + SerializingHTMLBuilder.init(self) diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py new file mode 100644 index 00000000..8d17f91b --- /dev/null +++ b/sphinx/builders/htmlhelp.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.htmlhelp + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Build HTML help support files. + Parts adapted from Python's Doc/tools/prechm.py. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import cgi +from os import path + +from docutils import nodes + +from sphinx import addnodes +from sphinx.builders.html import StandaloneHTMLBuilder + + +# Project file (*.hhp) template. 'outname' is the file basename (like +# the pythlp in pythlp.hhp); 'version' is the doc version number (like +# the 2.2 in Python 2.2). +# The magical numbers in the long line under [WINDOWS] set most of the +# user-visible features (visible buttons, tabs, etc). +# About 0x10384e: This defines the buttons in the help viewer. The +# following defns are taken from htmlhelp.h. Not all possibilities +# actually work, and not all those that work are available from the Help +# Workshop GUI. In particular, the Zoom/Font button works and is not +# available from the GUI. The ones we're using are marked with 'x': +# +# 0x000002 Hide/Show x +# 0x000004 Back x +# 0x000008 Forward x +# 0x000010 Stop +# 0x000020 Refresh +# 0x000040 Home x +# 0x000080 Forward +# 0x000100 Back +# 0x000200 Notes +# 0x000400 Contents +# 0x000800 Locate x +# 0x001000 Options x +# 0x002000 Print x +# 0x004000 Index +# 0x008000 Search +# 0x010000 History +# 0x020000 Favorites +# 0x040000 Jump 1 +# 0x080000 Jump 2 +# 0x100000 Zoom/Font x +# 0x200000 TOC Next +# 0x400000 TOC Prev + +project_template = '''\ +[OPTIONS] +Binary TOC=Yes +Binary Index=No +Compiled file=%(outname)s.chm +Contents file=%(outname)s.hhc +Default Window=%(outname)s +Default topic=index.html +Display compile progress=No +Full text search stop list file=%(outname)s.stp +Full-text search=Yes +Index file=%(outname)s.hhk +Language=0x409 +Title=%(title)s + +[WINDOWS] +%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\ +"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 + +[FILES] +''' + +contents_header = '''\ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<HTML> +<HEAD> +<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"> +<!-- Sitemap 1.0 --> +</HEAD><BODY> +<OBJECT type="text/site properties"> + <param name="Window Styles" value="0x801227"> + <param name="ImageType" value="Folder"> +</OBJECT> +<UL> +''' + +contents_footer = '''\ +</UL></BODY></HTML> +''' + +object_sitemap = '''\ +<OBJECT type="text/sitemap"> + <param name="Name" value="%s"> + <param name="Local" value="%s"> +</OBJECT> +''' + +# List of words the full text search facility shouldn't index. This +# becomes file outname.stp. Note that this list must be pretty small! +# Different versions of the MS docs claim the file has a maximum size of +# 256 or 512 bytes (including \r\n at the end of each line). +# Note that "and", "or", "not" and "near" are operators in the search +# language, so no point indexing them even if we wanted to. +stopwords = """ +a and are as at +be but by +for +if in into is it +near no not +of on or +such +that the their then there these they this to +was will with +""".split() + + +class HTMLHelpBuilder(StandaloneHTMLBuilder): + """ + Builder that also outputs Windows HTML help project, contents and + index files. Adapted from the original Doc/tools/prechm.py. + """ + name = 'htmlhelp' + + # don't copy the reST source + copysource = False + supported_image_types = ['image/png', 'image/gif', 'image/jpeg'] + + # don't add links + add_permalinks = False + # don't add sidebar etc. + embedded = True + + def init(self): + StandaloneHTMLBuilder.init(self) + # the output files for HTML help must be .html only + self.out_suffix = '.html' + + def handle_finish(self): + self.build_hhx(self.outdir, self.config.htmlhelp_basename) + + def build_hhx(self, outdir, outname): + self.info('dumping stopword list...') + f = open(path.join(outdir, outname+'.stp'), 'w') + try: + for word in sorted(stopwords): + print >>f, word + finally: + f.close() + + self.info('writing project file...') + f = open(path.join(outdir, outname+'.hhp'), 'w') + try: + f.write(project_template % {'outname': outname, + 'title': self.config.html_title, + 'version': self.config.version, + 'project': self.config.project}) + if not outdir.endswith(os.sep): + outdir += os.sep + olen = len(outdir) + for root, dirs, files in os.walk(outdir): + staticdir = (root == path.join(outdir, '_static')) + for fn in files: + if (staticdir and not fn.endswith('.js')) or \ + fn.endswith('.html'): + print >>f, path.join(root, fn)[olen:].replace(os.sep, + '\\') + finally: + f.close() + + self.info('writing TOC file...') + f = open(path.join(outdir, outname+'.hhc'), 'w') + try: + f.write(contents_header) + # special books + f.write('<LI> ' + object_sitemap % (self.config.html_short_title, + 'index.html')) + if self.config.html_use_modindex: + f.write('<LI> ' + object_sitemap % (_('Global Module Index'), + 'modindex.html')) + # the TOC + tocdoc = self.env.get_and_resolve_doctree( + self.config.master_doc, self, prune_toctrees=False) + def write_toc(node, ullevel=0): + if isinstance(node, nodes.list_item): + f.write('<LI> ') + for subnode in node: + write_toc(subnode, ullevel) + elif isinstance(node, nodes.reference): + link = node['refuri'] + title = cgi.escape(node.astext()).replace('"','"') + item = object_sitemap % (title, link) + f.write(item.encode('ascii', 'xmlcharrefreplace')) + elif isinstance(node, nodes.bullet_list): + if ullevel != 0: + f.write('<UL>\n') + for subnode in node: + write_toc(subnode, ullevel+1) + if ullevel != 0: + f.write('</UL>\n') + elif isinstance(node, addnodes.compact_paragraph): + for subnode in node: + write_toc(subnode, ullevel) + def istoctree(node): + return isinstance(node, addnodes.compact_paragraph) and \ + node.has_key('toctree') + for node in tocdoc.traverse(istoctree): + write_toc(node) + f.write(contents_footer) + finally: + f.close() + + self.info('writing index file...') + index = self.env.create_index(self) + f = open(path.join(outdir, outname+'.hhk'), 'w') + try: + f.write('<UL>\n') + def write_index(title, refs, subitems): + def write_param(name, value): + item = ' <param name="%s" value="%s">\n' % (name, value) + f.write(item.encode('ascii', 'xmlcharrefreplace')) + title = cgi.escape(title) + f.write('<LI> <OBJECT type="text/sitemap">\n') + write_param('Keyword', title) + if len(refs) == 0: + write_param('See Also', title) + elif len(refs) == 1: + write_param('Local', refs[0]) + else: + for i, ref in enumerate(refs): + # XXX: better title? + write_param('Name', '[%d] %s' % (i, ref)) + write_param('Local', ref) + f.write('</OBJECT>\n') + if subitems: + f.write('<UL> ') + for subitem in subitems: + write_index(subitem[0], subitem[1], []) + f.write('</UL>') + for (key, group) in index: + for title, (refs, subitems) in group: + write_index(title, refs, subitems) + f.write('</UL>\n') + finally: + f.close() diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py new file mode 100644 index 00000000..bc2f1ba5 --- /dev/null +++ b/sphinx/builders/latex.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.latex + ~~~~~~~~~~~~~~~~~~~~~ + + LaTeX builder. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import shutil +from os import path + +from docutils import nodes +from docutils.io import FileOutput +from docutils.utils import new_document +from docutils.frontend import OptionParser + +from sphinx import package_dir, addnodes +from sphinx.util import SEP, texescape +from sphinx.builders import Builder +from sphinx.environment import NoUri +from sphinx.util.console import bold, darkgreen +from sphinx.writers.latex import LaTeXWriter + + +class LaTeXBuilder(Builder): + """ + Builds LaTeX output to create PDF. + """ + name = 'latex' + format = 'latex' + supported_image_types = ['application/pdf', 'image/png', + 'image/gif', 'image/jpeg'] + + def init(self): + self.docnames = [] + self.document_data = [] + texescape.init() + + def get_outdated_docs(self): + return 'all documents' # for now + + def get_target_uri(self, docname, typ=None): + if typ == 'token': + # token references are always inside production lists and must be + # replaced by \token{} in LaTeX + return '@token' + if docname not in self.docnames: + raise NoUri + else: + return '%' + docname + + def get_relative_uri(self, from_, to, typ=None): + # ignore source path + return self.get_target_uri(to, typ) + + def init_document_data(self): + preliminary_document_data = map(list, self.config.latex_documents) + if not preliminary_document_data: + self.warn('no "latex_documents" config value found; no documents ' + 'will be written') + return + # assign subdirs to titles + self.titles = [] + for entry in preliminary_document_data: + docname = entry[0] + if docname not in self.env.all_docs: + self.warn('"latex_documents" config value references unknown ' + 'document %s' % docname) + continue + self.document_data.append(entry) + if docname.endswith(SEP+'index'): + docname = docname[:-5] + self.titles.append((docname, entry[2])) + + def write(self, *ignored): + docwriter = LaTeXWriter(self) + docsettings = OptionParser( + defaults=self.env.settings, + components=(docwriter,)).get_default_values() + + self.init_document_data() + + for entry in self.document_data: + docname, targetname, title, author, docclass = entry[:5] + toctree_only = False + if len(entry) > 5: + toctree_only = entry[5] + destination = FileOutput( + destination_path=path.join(self.outdir, targetname), + encoding='utf-8') + self.info("processing " + targetname + "... ", nonl=1) + doctree = self.assemble_doctree(docname, toctree_only, + appendices=((docclass == 'manual') and + self.config.latex_appendices or [])) + self.post_process_images(doctree) + self.info("writing... ", nonl=1) + doctree.settings = docsettings + doctree.settings.author = author + doctree.settings.title = title + doctree.settings.docname = docname + doctree.settings.docclass = docclass + docwriter.write(doctree, destination) + self.info("done") + + def assemble_doctree(self, indexfile, toctree_only, appendices): + self.docnames = set([indexfile] + appendices) + self.info(darkgreen(indexfile) + " ", nonl=1) + def process_tree(docname, tree): + tree = tree.deepcopy() + for toctreenode in tree.traverse(addnodes.toctree): + newnodes = [] + includefiles = map(str, toctreenode['includefiles']) + for includefile in includefiles: + try: + self.info(darkgreen(includefile) + " ", nonl=1) + subtree = process_tree( + includefile, self.env.get_doctree(includefile)) + self.docnames.add(includefile) + except Exception: + self.warn('toctree contains ref to nonexisting ' + 'file %r' % includefile, + self.env.doc2path(docname)) + else: + sof = addnodes.start_of_file(docname=includefile) + sof.children = subtree.children + newnodes.append(sof) + toctreenode.parent.replace(toctreenode, newnodes) + return tree + tree = self.env.get_doctree(indexfile) + tree['docname'] = indexfile + if toctree_only: + # extract toctree nodes from the tree and put them in a + # fresh document + new_tree = new_document('<latex output>') + new_sect = nodes.section() + new_sect += nodes.title(u'<Set title in conf.py>', + u'<Set title in conf.py>') + new_tree += new_sect + for node in tree.traverse(addnodes.toctree): + new_sect += node + tree = new_tree + largetree = process_tree(indexfile, tree) + largetree['docname'] = indexfile + for docname in appendices: + appendix = self.env.get_doctree(docname) + appendix['docname'] = docname + largetree.append(appendix) + self.info() + self.info("resolving references...") + self.env.resolve_references(largetree, indexfile, self) + # resolve :ref:s to distant tex files -- we can't add a cross-reference, + # but append the document name + for pendingnode in largetree.traverse(addnodes.pending_xref): + docname = pendingnode['refdocname'] + sectname = pendingnode['refsectname'] + newnodes = [nodes.emphasis(sectname, sectname)] + for subdir, title in self.titles: + if docname.startswith(subdir): + newnodes.append(nodes.Text(_(' (in '), _(' (in '))) + newnodes.append(nodes.emphasis(title, title)) + newnodes.append(nodes.Text(')', ')')) + break + else: + pass + pendingnode.replace_self(newnodes) + return largetree + + def finish(self): + # copy image files + if self.images: + self.info(bold('copying images...'), nonl=1) + for src, dest in self.images.iteritems(): + self.info(' '+src, nonl=1) + shutil.copyfile(path.join(self.srcdir, src), + path.join(self.outdir, dest)) + self.info() + + # copy additional files + if self.config.latex_additional_files: + self.info(bold('copying additional files...'), nonl=1) + for filename in self.config.latex_additional_files: + self.info(' '+filename, nonl=1) + shutil.copyfile(path.join(self.confdir, filename), + path.join(self.outdir, path.basename(filename))) + self.info() + + # the logo is handled differently + if self.config.latex_logo: + logobase = path.basename(self.config.latex_logo) + shutil.copyfile(path.join(self.confdir, self.config.latex_logo), + path.join(self.outdir, logobase)) + + self.info(bold('copying TeX support files... '), nonl=True) + staticdirname = path.join(package_dir, 'texinputs') + for filename in os.listdir(staticdirname): + if not filename.startswith('.'): + shutil.copyfile(path.join(staticdirname, filename), + path.join(self.outdir, filename)) + self.info('done') diff --git a/sphinx/linkcheck.py b/sphinx/builders/linkcheck.py index a4e876e2..f3962965 100644 --- a/sphinx/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.linkcheck - ~~~~~~~~~~~~~~~~ + sphinx.builders.linkcheck + ~~~~~~~~~~~~~~~~~~~~~~~~~ The CheckExternalLinksBuilder class. @@ -15,7 +15,7 @@ from urllib2 import build_opener, HTTPError from docutils import nodes -from sphinx.builder import Builder +from sphinx.builders import Builder from sphinx.util.console import purple, red, darkgreen # create an opener that will simulate a browser user-agent @@ -87,10 +87,12 @@ class CheckExternalLinksBuilder(Builder): self.write_entry('broken', docname, lineno, uri + ': ' + s) self.broken[uri] = (r, s) if self.app.quiet: - self.warn('%s:%s: broken link: %s' % (docname, lineno, uri)) + self.warn('broken link: %s' % uri, + '%s:%s' % (self.env.doc2path(docname), lineno)) else: self.info(' - ' + purple('redirected') + ' to ' + s) - self.write_entry('redirected', docname, lineno, uri + ' to ' + s) + self.write_entry('redirected', docname, + lineno, uri + ' to ' + s) self.redirected[uri] = (r, s) elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:': return @@ -98,7 +100,8 @@ class CheckExternalLinksBuilder(Builder): self.warn(uri + ' - ' + red('malformed!')) self.write_entry('malformed', docname, lineno, uri) if self.app.quiet: - self.warn('%s:%s: malformed link: %s' % (docname, lineno, uri)) + self.warn('malformed link: %s' % uri, + '%s:%s' % (self.env.doc2path(docname), lineno)) self.app.statuscode = 1 if self.broken: diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py new file mode 100644 index 00000000..7c2af1ef --- /dev/null +++ b/sphinx/builders/qthelp.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.qthelp + ~~~~~~~~~~~~~~~~~~~~~~ + + Build input files for the Qt collection generator. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import cgi +from os import path + +from docutils import nodes + +from sphinx import addnodes +from sphinx.builders.html import StandaloneHTMLBuilder + +_idpattern = re.compile( + r'(?P<title>.+) (\((?P<id>[\w\.]+)( (?P<descr>\w+))?\))$') + + +# Qt Help Collection Project (.qhcp). +# Is the input file for the help collection generator. +# It contains references to compressed help files which should be +# included in the collection. +# It may contain various other information for customizing Qt Assistant. +collection_template = '''\ +<?xml version="1.0" encoding="utf-8" ?> +<QHelpCollectionProject version="1.0"> + <docFiles> + <generate> + <file> + <input>%(outname)s.qhp</input> + <output>%(outname)s.qch</output> + </file> + </generate> + <register> + <file>%(outname)s.qch</file> + </register> + </docFiles> +</QHelpCollectionProject> +''' + +# Qt Help Project (.qhp) +# This is the input file for the help generator. +# It contains the table of contents, indices and references to the +# actual documentation files (*.html). +# In addition it defines a unique namespace for the documentation. +project_template = '''\ +<?xml version="1.0" encoding="UTF-8"?> +<QtHelpProject version="1.0"> + <namespace>%(outname)s.org.%(outname)s.%(nversion)s</namespace> + <virtualFolder>doc</virtualFolder> + <customFilter name="%(project)s %(version)s"> + <filterAttribute>%(outname)s</filterAttribute> + <filterAttribute>%(version)s</filterAttribute> + </customFilter> + <filterSection> + <filterAttribute>%(outname)s</filterAttribute> + <filterAttribute>%(version)s</filterAttribute> + <toc> + <section title="%(title)s" ref="%(masterdoc)s.html"> +%(sections)s + </section> + </toc> + <keywords> +%(keywords)s + </keywords> + <files> +%(files)s + </files> + </filterSection> +</QtHelpProject> +''' + +section_template = '<section title="%(title)s" ref="%(ref)s"/>' +file_template = ' '*12 + '<file>%(filename)s</file>' + + +class QtHelpBuilder(StandaloneHTMLBuilder): + """ + Builder that also outputs Qt help project, contents and index files. + """ + name = 'qthelp' + + # don't copy the reST source + copysource = False + supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', + 'image/jpeg'] + + # don't add links + add_permalinks = False + # don't add sidebar etc. + embedded = True + + def init(self): + StandaloneHTMLBuilder.init(self) + # the output files for HTML help must be .html only + self.out_suffix = '.html' + #self.config.html_style = 'traditional.css' + + def handle_finish(self): + self.build_qhcp(self.outdir, self.config.qthelp_basename) + self.build_qhp(self.outdir, self.config.qthelp_basename) + + def build_qhcp(self, outdir, outname): + self.info('writing collection project file...') + f = open(path.join(outdir, outname+'.qhcp'), 'w') + try: + f.write(collection_template % {'outname': outname}) + finally: + f.close() + + def build_qhp(self, outdir, outname): + self.info('writing project file...') + + # sections + tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self, + prune_toctrees=False) + istoctree = lambda node: ( + isinstance(node, addnodes.compact_paragraph) + and node.has_key('toctree')) + sections = [] + for node in tocdoc.traverse(istoctree): + sections.extend(self.write_toc(node)) + + if self.config.html_use_modindex: + item = section_template % {'title': _('Global Module Index'), + 'ref': 'modindex.html'} + sections.append(' '*4*4 + item) + sections = '\n'.join(sections) + + # keywords + keywords = [] + index = self.env.create_index(self) + for (key, group) in index: + for title, (refs, subitems) in group: + keywords.extend(self.build_keywords(title, refs, subitems)) + keywords = '\n'.join(keywords) + + # files + if not outdir.endswith(os.sep): + outdir += os.sep + olen = len(outdir) + projectfiles = [] + for root, dirs, files in os.walk(outdir): + staticdir = (root == path.join(outdir, '_static')) + for fn in files: + if (staticdir and not fn.endswith('.js')) or \ + fn.endswith('.html'): + filename = path.join(root, fn)[olen:] + #filename = filename.replace(os.sep, '\\') # XXX + projectfiles.append(file_template % {'filename': filename}) + projectfiles = '\n'.join(projectfiles) + + # write the project file + f = open(path.join(outdir, outname+'.qhp'), 'w') + try: + nversion = self.config.version.replace('.', '_') + nversion = nversion.replace(' ', '_') + f.write(project_template % {'outname': outname, + 'title': self.config.html_title, + 'version': self.config.version, + 'project': self.config.project, + 'nversion': nversion, + 'masterdoc': self.config.master_doc, + 'sections': sections, + 'keywords': keywords, + 'files': projectfiles}) + finally: + f.close() + + def isdocnode(self, node): + if not isinstance(node, nodes.list_item): + return False + if len(node.children) != 2: + return False + if not isinstance(node.children[0], addnodes.compact_paragraph): + return False + if not isinstance(node.children[0][0], nodes.reference): + return False + if not isinstance(node.children[1], nodes.bullet_list): + return False + return True + + def write_toc(self, node, indentlevel=4): + parts = [] + if self.isdocnode(node): + refnode = node.children[0][0] + link = refnode['refuri'] + title = cgi.escape(refnode.astext()).replace('"','"') + item = '<section title="%(title)s" ref="%(ref)s">' % { + 'title': title, + 'ref': link} + parts.append(' '*4*indentlevel + item) + for subnode in node.children[1]: + parts.extend(self.write_toc(subnode, indentlevel+1)) + parts.append(' '*4*indentlevel + '</section>') + elif isinstance(node, nodes.list_item): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + elif isinstance(node, nodes.reference): + link = node['refuri'] + title = cgi.escape(node.astext()).replace('"','"') + item = section_template % {'title': title, 'ref': link} + item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace') + parts.append(item.encode('ascii', 'xmlcharrefreplace')) + elif isinstance(node, nodes.bullet_list): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + elif isinstance(node, addnodes.compact_paragraph): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + + return parts + + def keyword_item(self, name, ref): + matchobj = _idpattern.match(name) + if matchobj: + groupdict = matchobj.groupdict() + shortname = groupdict['title'] + id = groupdict.get('id') +# descr = groupdict.get('descr') + if shortname.endswith('()'): + shortname = shortname[:-2] + id = '%s.%s' % (id, shortname) + else: + id = descr = None + + if id: + item = ' '*12 + '<keyword name="%s" id="%s" ref="%s"/>' % ( + name, id, ref) + else: + item = ' '*12 + '<keyword name="%s" ref="%s"/>' % (name, ref) + item.encode('ascii', 'xmlcharrefreplace') + return item + + def build_keywords(self, title, refs, subitems): + keywords = [] + + title = cgi.escape(title) +# if len(refs) == 0: # XXX +# write_param('See Also', title) + if len(refs) == 1: + keywords.append(self.keyword_item(title, refs[0])) + elif len(refs) > 1: + for i, ref in enumerate(refs): # XXX +# item = (' '*12 + +# '<keyword name="%s [%d]" ref="%s"/>' % ( +# title, i, ref)) +# item.encode('ascii', 'xmlcharrefreplace') +# keywords.append(item) + keywords.append(self.keyword_item(title, ref)) + + if subitems: + for subitem in subitems: + keywords.extend(self.build_keywords(subitem[0], subitem[1], [])) + + return keywords diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py new file mode 100644 index 00000000..8651778c --- /dev/null +++ b/sphinx/builders/text.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.text + ~~~~~~~~~~~~~~~~~~~~ + + Plain-text Sphinx builder. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs +from os import path + +from docutils.io import StringOutput + +from sphinx.util import ensuredir, os_path +from sphinx.builders import Builder +from sphinx.writers.text import TextWriter + + +class TextBuilder(Builder): + name = 'text' + format = 'text' + out_suffix = '.txt' + + def init(self): + pass + + def get_outdated_docs(self): + for docname in self.env.found_docs: + if docname not in self.env.all_docs: + yield docname + continue + targetname = self.env.doc2path(docname, self.outdir, + self.out_suffix) + try: + targetmtime = path.getmtime(targetname) + except Exception: + targetmtime = 0 + try: + srcmtime = path.getmtime(self.env.doc2path(docname)) + if srcmtime > targetmtime: + yield docname + except EnvironmentError: + # source doesn't exist anymore + pass + + def get_target_uri(self, docname, typ=None): + return '' + + def prepare_writing(self, docnames): + self.writer = TextWriter(self) + + def write_doc(self, docname, doctree): + destination = StringOutput(encoding='utf-8') + self.writer.write(doctree, destination) + outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix) + ensuredir(path.dirname(outfilename)) + try: + f = codecs.open(outfilename, 'w', 'utf-8') + try: + f.write(self.writer.output) + finally: + f.close() + except (IOError, OSError), err: + self.warn("error writing file %s: %s" % (outfilename, err)) + + def finish(self): + pass diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py index c9a35c61..813661bb 100644 --- a/sphinx/cmdline.py +++ b/sphinx/cmdline.py @@ -18,8 +18,9 @@ from os import path from docutils.utils import SystemMessage from sphinx import __version__ -from sphinx.application import Sphinx, SphinxError -from sphinx.util import format_exception_cut_frames, save_traceback +from sphinx.errors import SphinxError +from sphinx.application import Sphinx +from sphinx.util import Tee, format_exception_cut_frames, save_traceback from sphinx.util.console import darkred, nocolor, color_terminal @@ -31,8 +32,10 @@ def usage(argv, msg=None): Sphinx v%s Usage: %s [options] sourcedir outdir [filenames...] Options: -b <builder> -- builder to use; default is html - -a -- write all files; default is to only write new and changed files + -a -- write all files; default is to only write \ +new and changed files -E -- don't use a saved environment, always read all files + -t <tag> -- include "only" blocks with <tag> -d <path> -- path for the cached environment and doctree files (default: outdir/.doctrees) -c <path> -- path where configuration file (conf.py) is located @@ -43,6 +46,8 @@ Options: -b <builder> -- builder to use; default is html -N -- do not do colored output -q -- no output on stdout, just warnings on stderr -Q -- no output at all, not even warnings + -w <file> -- write warnings (and errors) to given file + -W -- turn warnings into errors -P -- run Pdb on exception Modi: * without -a and without filenames, write new and changed files. @@ -56,7 +61,7 @@ def main(argv): nocolor() try: - opts, args = getopt.getopt(argv[1:], 'ab:d:c:CD:A:NEqQP') + opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:g:NEqQWw:P') allopts = set(opt[0] for opt in opts) srcdir = confdir = path.abspath(args[0]) if not path.isdir(srcdir): @@ -64,7 +69,8 @@ def main(argv): return 1 if not path.isfile(path.join(srcdir, 'conf.py')) and \ '-c' not in allopts and '-C' not in allopts: - print >>sys.stderr, 'Error: Source directory doesn\'t contain conf.py file.' + print >>sys.stderr, ('Error: Source directory doesn\'t ' + 'contain conf.py file.') return 1 outdir = path.abspath(args[1]) if not path.isdir(outdir): @@ -84,11 +90,14 @@ def main(argv): return 1 buildername = all_files = None - freshenv = use_pdb = False + freshenv = warningiserror = use_pdb = False status = sys.stdout warning = sys.stderr + error = sys.stderr + warnfile = None confoverrides = {} htmlcontext = {} + tags = [] doctreedir = path.join(outdir, '.doctrees') for opt, val in opts: if opt == '-b': @@ -98,13 +107,15 @@ def main(argv): usage(argv, 'Cannot combine -a option and filenames.') return 1 all_files = True + elif opt == '-t': + tags.append(val) elif opt == '-d': doctreedir = path.abspath(val) elif opt == '-c': confdir = path.abspath(val) if not path.isfile(path.join(confdir, 'conf.py')): - print >>sys.stderr, \ - 'Error: Configuration directory doesn\'t contain conf.py file.' + print >>sys.stderr, ('Error: Configuration directory ' + 'doesn\'t contain conf.py file.') return 1 elif opt == '-C': confdir = None @@ -112,8 +123,8 @@ def main(argv): try: key, val = val.split('=') except ValueError: - print >>sys.stderr, \ - 'Error: -D option argument must be in the form name=value.' + print >>sys.stderr, ('Error: -D option argument must be ' + 'in the form name=value.') return 1 try: val = int(val) @@ -124,8 +135,8 @@ def main(argv): try: key, val = val.split('=') except ValueError: - print >>sys.stderr, \ - 'Error: -A option argument must be in the form name=value.' + print >>sys.stderr, ('Error: -A option argument must be ' + 'in the form name=value.') return 1 try: val = int(val) @@ -141,46 +152,57 @@ def main(argv): elif opt == '-Q': status = None warning = None + elif opt == '-W': + warningiserror = True + elif opt == '-w': + warnfile = val elif opt == '-P': use_pdb = True confoverrides['html_context'] = htmlcontext + if warning and warnfile: + warnfp = open(warnfile, 'w') + warning = Tee(warning, warnfp) + error = warning + try: app = Sphinx(srcdir, confdir, outdir, doctreedir, buildername, - confoverrides, status, warning, freshenv) + confoverrides, status, warning, freshenv, + warningiserror, tags) app.build(all_files, filenames) return app.statuscode except KeyboardInterrupt: if use_pdb: import pdb - print >>sys.stderr, darkred('Interrupted while building, starting debugger:') + print >>error, darkred('Interrupted while building, ' + 'starting debugger:') traceback.print_exc() pdb.post_mortem(sys.exc_info()[2]) return 1 except Exception, err: if use_pdb: import pdb - print >>sys.stderr, darkred('Exception occurred while building, ' - 'starting debugger:') + print >>error, darkred('Exception occurred while building, ' + 'starting debugger:') traceback.print_exc() pdb.post_mortem(sys.exc_info()[2]) else: if isinstance(err, SystemMessage): - print >>sys.stderr, darkred('reST markup error:') - print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace') + print >>error, darkred('reST markup error:') + print >>error, err.args[0].encode('ascii', 'backslashreplace') elif isinstance(err, SphinxError): - print >>sys.stderr, darkred('%s:' % err.category) - print >>sys.stderr, err + print >>error, darkred('%s:' % err.category) + print >>error, err else: - print >>sys.stderr, darkred('Exception occurred:') - print >>sys.stderr, format_exception_cut_frames().rstrip() + print >>error, darkred('Exception occurred:') + print >>error, format_exception_cut_frames().rstrip() tbpath = save_traceback() - print >>sys.stderr, darkred('The full traceback has been saved ' - 'in %s, if you want to report the ' - 'issue to the author.' % tbpath) - print >>sys.stderr, ('Please also report this if it was a user ' - 'error, so that a better error message ' - 'can be provided next time.') - print >>sys.stderr, ('Send reports to sphinx-dev@googlegroups.com. ' - 'Thanks!') + print >>error, darkred('The full traceback has been saved ' + 'in %s, if you want to report the ' + 'issue to the author.' % tbpath) + print >>error, ('Please also report this if it was a user ' + 'error, so that a better error message ' + 'can be provided next time.') + print >>error, ('Send reports to sphinx-dev@googlegroups.com. ' + 'Thanks!') return 1 diff --git a/sphinx/config.py b/sphinx/config.py index a9c7ae95..3e3abb0d 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -6,97 +6,114 @@ Build configuration file handling. :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. - :license: BSD license. + :license: BSD, see LICENSE for details. """ import os from os import path +from sphinx.util import make_filename + class Config(object): """Configuration file abstraction.""" - # the values are: (default, needs fresh doctrees if changed) + # the values are: (default, what needs to be rebuilt if changed) # If you add a value here, don't forget to include it in the # quickstart.py file template as well as in the docs! config_values = dict( # general options - project = ('Python', True), - copyright = ('', False), - version = ('', True), - release = ('', True), - today = ('', True), - today_fmt = (None, True), # the real default is locale-dependent - - language = (None, True), - locale_dirs = ([], True), - - master_doc = ('contents', True), - source_suffix = ('.rst', True), - source_encoding = ('utf-8', True), - unused_docs = ([], True), - exclude_dirs = ([], True), - exclude_trees = ([], True), - exclude_dirnames = ([], True), - default_role = (None, True), - add_function_parentheses = (True, True), - add_module_names = (True, True), - show_authors = (False, True), - pygments_style = ('sphinx', False), - highlight_language = ('python', False), - templates_path = ([], False), - template_bridge = (None, False), - keep_warnings = (False, True), + project = ('Python', 'env'), + copyright = ('', 'html'), + version = ('', 'env'), + release = ('', 'env'), + today = ('', 'env'), + today_fmt = (None, 'env'), # the real default is locale-dependent + + language = (None, 'env'), + locale_dirs = ([], 'env'), + + master_doc = ('contents', 'env'), + source_suffix = ('.rst', 'env'), + source_encoding = ('utf-8', 'env'), + unused_docs = ([], 'env'), + exclude_dirs = ([], 'env'), + exclude_trees = ([], 'env'), + exclude_dirnames = ([], 'env'), + default_role = (None, 'env'), + add_function_parentheses = (True, 'env'), + add_module_names = (True, 'env'), + trim_footnote_reference_space = (False, 'env'), + show_authors = (False, 'env'), + pygments_style = (None, 'html'), + highlight_language = ('python', 'env'), + templates_path = ([], 'html'), + template_bridge = (None, 'html'), + keep_warnings = (False, 'env'), + modindex_common_prefix = ([], 'html'), + rst_epilog = (None, 'env'), # HTML options + html_theme = ('default', 'html'), + html_theme_path = ([], 'html'), + html_theme_options = ({}, 'html'), html_title = (lambda self: '%s v%s documentation' % (self.project, self.release), - False), - html_short_title = (lambda self: self.html_title, False), - html_style = ('default.css', False), - html_logo = (None, False), - html_favicon = (None, False), - html_static_path = ([], False), - html_last_updated_fmt = (None, False), # the real default is locale-dependent - html_use_smartypants = (True, False), - html_translator_class = (None, False), - html_sidebars = ({}, False), - html_additional_pages = ({}, False), - html_use_modindex = (True, False), - html_use_index = (True, False), - html_split_index = (False, False), - html_copy_source = (True, False), - html_use_opensearch = ('', False), - html_file_suffix = (None, False), - html_show_sphinx = (True, False), - html_context = ({}, False), + 'html'), + html_short_title = (lambda self: self.html_title, 'html'), + html_style = (None, 'html'), + html_logo = (None, 'html'), + html_favicon = (None, 'html'), + html_static_path = ([], 'html'), + # the real default is locale-dependent + html_last_updated_fmt = (None, 'html'), + html_use_smartypants = (True, 'html'), + html_translator_class = (None, 'html'), + html_sidebars = ({}, 'html'), + html_additional_pages = ({}, 'html'), + html_use_modindex = (True, 'html'), + html_add_permalinks = (True, 'html'), + html_use_index = (True, 'html'), + html_split_index = (False, 'html'), + html_copy_source = (True, 'html'), + html_show_sourcelink = (True, 'html'), + html_use_opensearch = ('', 'html'), + html_file_suffix = (None, 'html'), + html_link_suffix = (None, 'html'), + html_show_sphinx = (True, 'html'), + html_context = ({}, 'html'), # HTML help only options - htmlhelp_basename = ('pydoc', False), + htmlhelp_basename = (lambda self: make_filename(self.project), None), + + # Qt help only options + qthelp_basename = (lambda self: make_filename(self.project), None), # LaTeX options - latex_documents = ([], False), - latex_logo = (None, False), - latex_appendices = ([], False), - latex_use_parts = (False, False), - latex_use_modindex = (True, False), + latex_documents = ([], None), + latex_logo = (None, None), + latex_appendices = ([], None), + latex_use_parts = (False, None), + latex_use_modindex = (True, None), # paper_size and font_size are still separate values # so that you can give them easily on the command line - latex_paper_size = ('letter', False), - latex_font_size = ('10pt', False), - latex_elements = ({}, False), + latex_paper_size = ('letter', None), + latex_font_size = ('10pt', None), + latex_elements = ({}, None), + latex_additional_files = ([], None), # now deprecated - use latex_elements - latex_preamble = ('', False), + latex_preamble = ('', None), ) - def __init__(self, dirname, filename, overrides): + def __init__(self, dirname, filename, overrides, tags): self.overrides = overrides self.values = Config.config_values.copy() config = {} if dirname is not None: config['__file__'] = path.join(dirname, filename) + config['tags'] = tags olddir = os.getcwd() try: os.chdir(dirname) @@ -111,7 +128,12 @@ class Config(object): def init_values(self): config = self._raw_config - config.update(self.overrides) + for valname, value in self.overrides.iteritems(): + if '.' in valname: + realvalname, key = valname.split('.', 1) + config.setdefault(realvalname, {})[key] = value + else: + config[valname] = value for name in config: if name in self.values: self.__dict__[name] = config[name] @@ -123,7 +145,7 @@ class Config(object): if name not in self.values: raise AttributeError('No such config value: %s' % name) default = self.values[name][0] - if callable(default): + if hasattr(default, '__call__'): return default(self) return default diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index df86f997..2de29010 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -9,6 +9,19 @@ :license: BSD, see LICENSE for details. """ +from docutils.parsers.rst import directives +from docutils.parsers.rst.directives import images + +# import and register directives from sphinx.directives.desc import * from sphinx.directives.code import * from sphinx.directives.other import * + + +# allow units for the figure's "figwidth" +try: + images.Figure.option_spec['figwidth'] = \ + directives.length_or_percentage_or_unitless +except AttributeError: + images.figure.options['figwidth'] = \ + directives.length_or_percentage_or_unitless diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 8ce2eed9..645bc784 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -7,6 +7,7 @@ :license: BSD, see LICENSE for details. """ +import os import sys import codecs from os import path @@ -15,85 +16,161 @@ from docutils import nodes from docutils.parsers.rst import directives from sphinx import addnodes - - -# ------ highlight directive -------------------------------------------------------- - -def highlightlang_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - if 'linenothreshold' in options: +from sphinx.util import parselinenos +from sphinx.util.compat import Directive, directive_dwim + + +class Highlight(Directive): + """ + Directive to set the highlighting language for code blocks, as well + as the threshold for line numbers. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'linenothreshold': directives.unchanged, + } + + def run(self): + if 'linenothreshold' in self.options: + try: + linenothreshold = int(self.options['linenothreshold']) + except Exception: + linenothreshold = 10 + else: + linenothreshold = sys.maxint + return [addnodes.highlightlang(lang=self.arguments[0].strip(), + linenothreshold=linenothreshold)] + + +class CodeBlock(Directive): + """ + Directive for a code block with special highlighting or line numbering + settings. + """ + + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'linenos': directives.flag, + } + + def run(self): + code = u'\n'.join(self.content) + literal = nodes.literal_block(code, code) + literal['language'] = self.arguments[0] + literal['linenos'] = 'linenos' in self.options + return [literal] + + +class LiteralInclude(Directive): + """ + Like ``.. include:: :literal:``, but only warns if the include file is + not found, and does not raise errors. Also has several options for + selecting what to include. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'linenos': directives.flag, + 'language': directives.unchanged_required, + 'encoding': directives.encoding, + 'pyobject': directives.unchanged_required, + 'lines': directives.unchanged_required, + 'start-after': directives.unchanged_required, + 'end-before': directives.unchanged_required, + } + + def run(self): + document = self.state.document + filename = self.arguments[0] + if not document.settings.file_insertion_enabled: + return [document.reporter.warning('File insertion disabled', + line=self.lineno)] + env = document.settings.env + if filename.startswith('/') or filename.startswith(os.sep): + rel_fn = filename[1:] + else: + docdir = path.dirname(env.doc2path(env.docname, base=None)) + rel_fn = path.normpath(path.join(docdir, filename)) + fn = path.join(env.srcdir, rel_fn) + + if 'pyobject' in self.options and 'lines' in self.options: + return [document.reporter.warning( + 'Cannot use both "pyobject" and "lines" options', + line=self.lineno)] + + encoding = self.options.get('encoding', env.config.source_encoding) try: - linenothreshold = int(options['linenothreshold']) - except Exception: - linenothreshold = 10 - else: - linenothreshold = sys.maxint - return [addnodes.highlightlang(lang=arguments[0].strip(), - linenothreshold=linenothreshold)] - -highlightlang_directive.content = 0 -highlightlang_directive.arguments = (1, 0, 0) -highlightlang_directive.options = {'linenothreshold': directives.unchanged} -directives.register_directive('highlight', highlightlang_directive) -directives.register_directive('highlightlang', highlightlang_directive) # old name - - -# ------ code-block directive ------------------------------------------------------- - -def codeblock_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - code = u'\n'.join(content) - literal = nodes.literal_block(code, code) - literal['language'] = arguments[0] - literal['linenos'] = 'linenos' in options - return [literal] - -codeblock_directive.content = 1 -codeblock_directive.arguments = (1, 0, 0) -codeblock_directive.options = {'linenos': directives.flag} -directives.register_directive('code-block', codeblock_directive) -directives.register_directive('sourcecode', codeblock_directive) - - -# ------ literalinclude directive --------------------------------------------------- - -def literalinclude_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - """Like .. include:: :literal:, but only warns if the include file is not found.""" - if not state.document.settings.file_insertion_enabled: - return [state.document.reporter.warning('File insertion disabled', line=lineno)] - env = state.document.settings.env - rel_fn = arguments[0] - source_dir = path.dirname(path.abspath(state_machine.input_lines.source( - lineno - state_machine.input_offset - 1))) - fn = path.normpath(path.join(source_dir, rel_fn)) - - encoding = options.get('encoding', env.config.source_encoding) - try: - f = codecs.open(fn, 'rU', encoding) - text = f.read() - f.close() - except (IOError, OSError): - retnode = state.document.reporter.warning( - 'Include file %r not found or reading it failed' % arguments[0], line=lineno) - except UnicodeError: - retnode = state.document.reporter.warning( - 'Encoding %r used for reading included file %r seems to ' - 'be wrong, try giving an :encoding: option' % - (encoding, arguments[0])) - else: + f = codecs.open(fn, 'rU', encoding) + lines = f.readlines() + f.close() + except (IOError, OSError): + return [document.reporter.warning( + 'Include file %r not found or reading it failed' % filename, + line=self.lineno)] + except UnicodeError: + return [document.reporter.warning( + 'Encoding %r used for reading included file %r seems to ' + 'be wrong, try giving an :encoding: option' % + (encoding, filename))] + + objectname = self.options.get('pyobject') + if objectname is not None: + from sphinx.pycode import ModuleAnalyzer + analyzer = ModuleAnalyzer.for_file(fn, '') + tags = analyzer.find_tags() + if objectname not in tags: + return [document.reporter.warning( + 'Object named %r not found in include file %r' % + (objectname, filename), line=self.lineno)] + else: + lines = lines[tags[objectname][1]-1 : tags[objectname][2]-1] + + linespec = self.options.get('lines') + if linespec is not None: + try: + linelist = parselinenos(linespec, len(lines)) + except ValueError, err: + return [document.reporter.warning(str(err), line=self.lineno)] + lines = [lines[i] for i in linelist] + + startafter = self.options.get('start-after') + endbefore = self.options.get('end-before') + if startafter is not None or endbefore is not None: + use = not startafter + res = [] + for line in lines: + if not use and startafter in line: + use = True + elif use and endbefore in line: + use = False + break + elif use: + res.append(line) + lines = res + + text = ''.join(lines) retnode = nodes.literal_block(text, text, source=fn) retnode.line = 1 - if options.get('language', ''): - retnode['language'] = options['language'] - if 'linenos' in options: + if self.options.get('language', ''): + retnode['language'] = self.options['language'] + if 'linenos' in self.options: retnode['linenos'] = True - state.document.settings.env.note_dependency(rel_fn) - return [retnode] - -literalinclude_directive.options = {'linenos': directives.flag, - 'language': directives.unchanged, - 'encoding': directives.encoding} -literalinclude_directive.content = 0 -literalinclude_directive.arguments = (1, 0, 0) -directives.register_directive('literalinclude', literalinclude_directive) + document.settings.env.note_dependency(rel_fn) + return [retnode] + + +directives.register_directive('highlight', directive_dwim(Highlight)) +directives.register_directive('highlightlang', directive_dwim(Highlight)) # old +directives.register_directive('code-block', directive_dwim(CodeBlock)) +directives.register_directive('sourcecode', directive_dwim(CodeBlock)) +directives.register_directive('literalinclude', directive_dwim(LiteralInclude)) diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py index fc6cb4f6..60b43a5d 100644 --- a/sphinx/directives/desc.py +++ b/sphinx/directives/desc.py @@ -15,112 +15,11 @@ from docutils.parsers.rst import directives from sphinx import addnodes from sphinx.util import ws_re - - -# ------ information units --------------------------------------------------------- - -def desc_index_text(desctype, module, name, add_modules): - if desctype == 'function': - if not module: - return _('%s() (built-in function)') % name - return _('%s() (in module %s)') % (name, module) - elif desctype == 'data': - if not module: - return _('%s (built-in variable)') % name - return _('%s (in module %s)') % (name, module) - elif desctype == 'class': - if not module: - return _('%s (built-in class)') % name - return _('%s (class in %s)') % (name, module) - elif desctype == 'exception': - return name - elif desctype == 'method': - try: - clsname, methname = name.rsplit('.', 1) - except ValueError: - if module: - return _('%s() (in module %s)') % (name, module) - else: - return '%s()' % name - if module and add_modules: - return _('%s() (%s.%s method)') % (methname, module, clsname) - else: - return _('%s() (%s method)') % (methname, clsname) - elif desctype == 'staticmethod': - try: - clsname, methname = name.rsplit('.', 1) - except ValueError: - if module: - return _('%s() (in module %s)') % (name, module) - else: - return '%s()' % name - if module and add_modules: - return _('%s() (%s.%s static method)') % (methname, module, clsname) - else: - return _('%s() (%s static method)') % (methname, clsname) - elif desctype == 'attribute': - try: - clsname, attrname = name.rsplit('.', 1) - except ValueError: - if module: - return _('%s (in module %s)') % (name, module) - else: - return name - if module and add_modules: - return _('%s (%s.%s attribute)') % (attrname, module, clsname) - else: - return _('%s (%s attribute)') % (attrname, clsname) - elif desctype == 'cfunction': - return _('%s (C function)') % name - elif desctype == 'cmember': - return _('%s (C member)') % name - elif desctype == 'cmacro': - return _('%s (C macro)') % name - elif desctype == 'ctype': - return _('%s (C type)') % name - elif desctype == 'cvar': - return _('%s (C variable)') % name - else: - raise ValueError('unhandled descenv: %s' % desctype) - - -# ------ make field lists (like :param foo:) in desc bodies prettier - -_ = lambda x: x # make gettext extraction in constants possible - -doc_fields_with_arg = { - 'param': '%param', - 'parameter': '%param', - 'arg': '%param', - 'argument': '%param', - 'keyword': '%param', - 'kwarg': '%param', - 'kwparam': '%param', - 'type': '%type', - 'raises': _('Raises'), - 'raise': 'Raises', - 'exception': 'Raises', - 'except': 'Raises', - 'var': _('Variable'), - 'ivar': 'Variable', - 'cvar': 'Variable', - 'returns': _('Returns'), - 'return': 'Returns', -} - -doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except') - -doc_fields_without_arg = { - 'returns': 'Returns', - 'return': 'Returns', - 'rtype': _('Return type'), -} - -del _ +from sphinx.util.compat import Directive, directive_dwim def _is_only_paragraph(node): - # determine if the node only contains one paragraph (and system messages) + """True if the node only contains one paragraph (and system messages).""" if len(node) == 0: return False elif len(node) > 1: @@ -132,168 +31,18 @@ def _is_only_paragraph(node): return False -def handle_doc_fields(node, env): - # don't traverse, only handle field lists that are immediate children - for child in node.children: - if not isinstance(child, nodes.field_list): - continue - params = None - param_nodes = {} - param_types = {} - new_list = nodes.field_list() - for field in child: - fname, fbody = field - try: - typ, obj = fname.astext().split(None, 1) - typdesc = _(doc_fields_with_arg[typ]) - if _is_only_paragraph(fbody): - children = fbody.children[0].children - else: - children = fbody.children - if typdesc == '%param': - if not params: - pfield = nodes.field() - pfield += nodes.field_name('', _('Parameters')) - pfield += nodes.field_body() - params = nodes.bullet_list() - pfield[1] += params - new_list += pfield - dlitem = nodes.list_item() - dlpar = nodes.paragraph() - dlpar += nodes.emphasis(obj, obj) - dlpar += nodes.Text(' -- ', ' -- ') - dlpar += children - param_nodes[obj] = dlpar - dlitem += dlpar - params += dlitem - elif typdesc == '%type': - typenodes = fbody.children - if _is_only_paragraph(fbody): - typenodes = [nodes.Text(' (')] + \ - typenodes[0].children + [nodes.Text(')')] - param_types[obj] = typenodes - else: - fieldname = typdesc + ' ' - nfield = nodes.field() - nfieldname = nodes.field_name(fieldname, fieldname) - nfield += nfieldname - node = nfieldname - if typ in doc_fields_with_linked_arg: - node = addnodes.pending_xref(obj, reftype='obj', - refcaption=False, - reftarget=obj, - modname=env.currmodule, - classname=env.currclass) - nfieldname += node - node += nodes.Text(obj, obj) - nfield += nodes.field_body() - nfield[1] += fbody.children - new_list += nfield - except (KeyError, ValueError): - fnametext = fname.astext() - try: - typ = _(doc_fields_without_arg[fnametext]) - except KeyError: - # at least capitalize the field name - typ = fnametext.capitalize() - fname[0] = nodes.Text(typ) - new_list += field - for param, type in param_types.iteritems(): - if param in param_nodes: - param_nodes[param][1:1] = type - child.replace_self(new_list) - - -# ------ functions to parse a Python or C signature and create desc_* nodes. - +# REs for Python signatures py_sig_re = re.compile( r'''^ ([\w.]*\.)? # class name(s) (\w+) \s* # thing name - (?: \((.*)\) # optional arguments - (\s* -> \s* .*)? )? $ # optional return annotation + (?: \((.*)\) # optional: arguments + (?:\s* -> \s* (.*))? # return annotation + )? $ # and nothing more ''', re.VERBOSE) py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ',' -def parse_py_signature(signode, sig, desctype, module, env): - """ - Transform a python signature into RST nodes. - Return (fully qualified name of the thing, classname if any). - - If inside a class, the current class name is handled intelligently: - * it is stripped from the displayed name if present - * it is added to the full name (return value) if not present - """ - m = py_sig_re.match(sig) - if m is None: - raise ValueError - classname, name, arglist, retann = m.groups() - - if retann: - retann = u' \N{RIGHTWARDS ARROW} ' + retann.strip()[2:] - - if env.currclass: - add_module = False - if classname and classname.startswith(env.currclass): - fullname = classname + name - # class name is given again in the signature - classname = classname[len(env.currclass):].lstrip('.') - elif classname: - # class name is given in the signature, but different - # (shouldn't happen) - fullname = env.currclass + '.' + classname + name - else: - # class name is not given in the signature - fullname = env.currclass + '.' + name - else: - add_module = True - fullname = classname and classname + name or name - - if desctype == 'staticmethod': - signode += addnodes.desc_annotation('static ', 'static ') - - if classname: - signode += addnodes.desc_addname(classname, classname) - # exceptions are a special case, since they are documented in the - # 'exceptions' module. - elif add_module and env.config.add_module_names and \ - module and module != 'exceptions': - nodetext = module + '.' - signode += addnodes.desc_addname(nodetext, nodetext) - - signode += addnodes.desc_name(name, name) - if not arglist: - if desctype in ('function', 'method', 'staticmethod'): - # for callables, add an empty parameter list - signode += addnodes.desc_parameterlist() - if retann: - signode += addnodes.desc_type(retann, retann) - return fullname, classname - signode += addnodes.desc_parameterlist() - - stack = [signode[-1]] - for token in py_paramlist_re.split(arglist): - if token == '[': - opt = addnodes.desc_optional() - stack[-1] += opt - stack.append(opt) - elif token == ']': - try: - stack.pop() - except IndexError: - raise ValueError - elif not token or token == ',' or token.isspace(): - pass - else: - token = token.strip() - stack[-1] += addnodes.desc_parameter(token, token) - if len(stack) != 1: - raise ValueError - if retann: - signode += addnodes.desc_type(retann, retann) - return fullname, classname - - +# REs for C signatures c_sig_re = re.compile( r'''^([^(]*?) # return type ([\w:]+) \s* # thing name (colon allowed for C++ class names) @@ -308,250 +57,689 @@ c_funcptr_sig_re = re.compile( ''', re.VERBOSE) c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$') +# RE for option descriptions +option_desc_re = re.compile( + r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)') + # RE to split at word boundaries wsplit_re = re.compile(r'(\W+)') -# These C types aren't described in the reference, so don't try to create -# a cross-reference to them -stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct')) - -def parse_c_type(node, ctype): - # add cross-ref nodes for all words - for part in filter(None, wsplit_re.split(ctype)): - tnode = nodes.Text(part, part) - if part[0] in string.ascii_letters+'_' and part not in stopwords: - pnode = addnodes.pending_xref( - '', reftype='ctype', reftarget=part, modname=None, classname=None) - pnode += tnode - node += pnode +# RE to strip backslash escapes +strip_backslash_re = re.compile(r'\\(?=[^\\])') + + +class DescDirective(Directive): + """ + Directive to describe a class, function or similar object. Not used + directly, but subclassed to add custom behavior. + """ + + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = { + 'noindex': directives.flag, + 'module': directives.unchanged, + } + + _ = lambda x: x # make gettext extraction in constants possible + + doc_fields_with_arg = { + 'param': '%param', + 'parameter': '%param', + 'arg': '%param', + 'argument': '%param', + 'keyword': '%param', + 'kwarg': '%param', + 'kwparam': '%param', + 'type': '%type', + 'raises': _('Raises'), + 'raise': 'Raises', + 'exception': 'Raises', + 'except': 'Raises', + 'var': _('Variable'), + 'ivar': 'Variable', + 'cvar': 'Variable', + 'returns': _('Returns'), + 'return': 'Returns', + } + + doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except') + + doc_fields_without_arg = { + 'returns': 'Returns', + 'return': 'Returns', + 'rtype': _('Return type'), + } + + def handle_doc_fields(self, node): + """ + Convert field lists with known keys inside the description content into + better-looking equivalents. + """ + # don't traverse, only handle field lists that are immediate children + for child in node.children: + if not isinstance(child, nodes.field_list): + continue + params = [] + pfield = None + param_nodes = {} + param_types = {} + new_list = nodes.field_list() + for field in child: + fname, fbody = field + try: + typ, obj = fname.astext().split(None, 1) + typdesc = _(self.doc_fields_with_arg[typ]) + if _is_only_paragraph(fbody): + children = fbody.children[0].children + else: + children = fbody.children + if typdesc == '%param': + if not params: + # add the field that later gets all the parameters + pfield = nodes.field() + new_list += pfield + dlitem = nodes.list_item() + dlpar = nodes.paragraph() + dlpar += nodes.emphasis(obj, obj) + dlpar += nodes.Text(' -- ', ' -- ') + dlpar += children + param_nodes[obj] = dlpar + dlitem += dlpar + params.append(dlitem) + elif typdesc == '%type': + typenodes = fbody.children + if _is_only_paragraph(fbody): + typenodes = ([nodes.Text(' (')] + + typenodes[0].children + + [nodes.Text(')')]) + param_types[obj] = typenodes + else: + fieldname = typdesc + ' ' + nfield = nodes.field() + nfieldname = nodes.field_name(fieldname, fieldname) + nfield += nfieldname + node = nfieldname + if typ in self.doc_fields_with_linked_arg: + node = addnodes.pending_xref( + obj, reftype='obj', refcaption=False, + reftarget=obj, modname=self.env.currmodule, + classname=self.env.currclass) + nfieldname += node + node += nodes.Text(obj, obj) + nfield += nodes.field_body() + nfield[1] += fbody.children + new_list += nfield + except (KeyError, ValueError): + fnametext = fname.astext() + try: + typ = _(self.doc_fields_without_arg[fnametext]) + except KeyError: + # at least capitalize the field name + typ = fnametext.capitalize() + fname[0] = nodes.Text(typ) + new_list += field + if params: + if len(params) == 1: + pfield += nodes.field_name('', _('Parameter')) + pfield += nodes.field_body() + pfield[1] += params[0][0] + else: + pfield += nodes.field_name('', _('Parameters')) + pfield += nodes.field_body() + pfield[1] += nodes.bullet_list() + pfield[1][0].extend(params) + + for param, type in param_types.iteritems(): + if param in param_nodes: + param_nodes[param][1:1] = type + child.replace_self(new_list) + + def get_signatures(self): + """ + Retrieve the signatures to document from the directive arguments. + """ + # remove backslashes to support (dummy) escapes; helps Vim highlighting + return [strip_backslash_re.sub('', sig.strip()) + for sig in self.arguments[0].split('\n')] + + def parse_signature(self, sig, signode): + """ + Parse the signature *sig* into individual nodes and append them to + *signode*. If ValueError is raised, parsing is aborted and the whole + *sig* is put into a single desc_name node. + """ + raise ValueError + + def add_target_and_index(self, name, sig, signode): + """ + Add cross-reference IDs and entries to self.indexnode, if applicable. + """ + return # do nothing by default + + def before_content(self): + """ + Called before parsing content. Used to set information about the current + directive context on the build environment. + """ + pass + + def after_content(self): + """ + Called after parsing content. Used to reset information about the + current directive context on the build environment. + """ + pass + + def run(self): + self.desctype = self.name + self.env = self.state.document.settings.env + self.indexnode = addnodes.index(entries=[]) + + node = addnodes.desc() + node.document = self.state.document + node['desctype'] = self.desctype + node['noindex'] = noindex = ('noindex' in self.options) + + self.names = [] + signatures = self.get_signatures() + for i, sig in enumerate(signatures): + # add a signature node for each signature in the current unit + # and add a reference target for it + signode = addnodes.desc_signature(sig, '') + signode['first'] = False + node.append(signode) + try: + # name can also be a tuple, e.g. (classname, objname) + name = self.parse_signature(sig, signode) + except ValueError, err: + # signature parsing failed + signode.clear() + signode += addnodes.desc_name(sig, sig) + continue # we don't want an index entry here + if not noindex and name not in self.names: + # only add target and index entry if this is the first + # description of the object with this name in this desc block + self.names.append(name) + self.add_target_and_index(name, sig, signode) + + contentnode = addnodes.desc_content() + node.append(contentnode) + if self.names: + # needed for association of version{added,changed} directives + self.env.currdesc = self.names[0] + self.before_content() + self.state.nested_parse(self.content, self.content_offset, contentnode) + self.handle_doc_fields(contentnode) + self.env.currdesc = None + self.after_content() + return [self.indexnode, node] + + +class PythonDesc(DescDirective): + """ + Description of a general Python object. + """ + + def get_signature_prefix(self, sig): + """ + May return a prefix to put before the object name in the signature. + """ + return '' + + def needs_arglist(self): + """ + May return true if an empty argument list is to be generated even if + the document contains none. + """ + return False + + def parse_signature(self, sig, signode): + """ + Transform a Python signature into RST nodes. + Returns (fully qualified name of the thing, classname if any). + + If inside a class, the current class name is handled intelligently: + * it is stripped from the displayed name if present + * it is added to the full name (return value) if not present + """ + m = py_sig_re.match(sig) + if m is None: + raise ValueError + classname, name, arglist, retann = m.groups() + + if self.env.currclass: + add_module = False + if classname and classname.startswith(self.env.currclass): + fullname = classname + name + # class name is given again in the signature + classname = classname[len(self.env.currclass):].lstrip('.') + elif classname: + # class name is given in the signature, but different + # (shouldn't happen) + fullname = self.env.currclass + '.' + classname + name + else: + # class name is not given in the signature + fullname = self.env.currclass + '.' + name else: - node += tnode - -def parse_c_signature(signode, sig, desctype): - """Transform a C (or C++) signature into RST nodes.""" - # first try the function pointer signature regex, it's more specific - m = c_funcptr_sig_re.match(sig) - if m is None: - m = c_sig_re.match(sig) - if m is None: - raise ValueError('no match') - rettype, name, arglist, const = m.groups() - - signode += addnodes.desc_type('', '') - parse_c_type(signode[-1], rettype) - try: - classname, funcname = name.split('::', 1) - classname += '::' - signode += addnodes.desc_addname(classname, classname) - signode += addnodes.desc_name(funcname, funcname) - # name (the full name) is still both parts - except ValueError: + add_module = True + fullname = classname and classname + name or name + + prefix = self.get_signature_prefix(sig) + if prefix: + signode += addnodes.desc_annotation(prefix, prefix) + + if classname: + signode += addnodes.desc_addname(classname, classname) + # exceptions are a special case, since they are documented in the + # 'exceptions' module. + elif add_module and self.env.config.add_module_names: + modname = self.options.get('module', self.env.currmodule) + if modname and modname != 'exceptions': + nodetext = modname + '.' + signode += addnodes.desc_addname(nodetext, nodetext) + signode += addnodes.desc_name(name, name) - # clean up parentheses from canonical name - m = c_funcptr_name_re.match(name) - if m: - name = m.group(1) - if not arglist: - if desctype == 'cfunction': - # for functions, add an empty parameter list - signode += addnodes.desc_parameterlist() - return name + if not arglist: + if self.needs_arglist(): + # for callables, add an empty parameter list + signode += addnodes.desc_parameterlist() + if retann: + signode += addnodes.desc_returns(retann, retann) + return fullname, classname + signode += addnodes.desc_parameterlist() + + stack = [signode[-1]] + for token in py_paramlist_re.split(arglist): + if token == '[': + opt = addnodes.desc_optional() + stack[-1] += opt + stack.append(opt) + elif token == ']': + try: + stack.pop() + except IndexError: + raise ValueError + elif not token or token == ',' or token.isspace(): + pass + else: + token = token.strip() + stack[-1] += addnodes.desc_parameter(token, token) + if len(stack) != 1: + raise ValueError + if retann: + signode += addnodes.desc_returns(retann, retann) + return fullname, classname - paramlist = addnodes.desc_parameterlist() - arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup - # this messes up function pointer types, but not too badly ;) - args = arglist.split(',') - for arg in args: - arg = arg.strip() - param = addnodes.desc_parameter('', '', noemph=True) - try: - ctype, argname = arg.rsplit(' ', 1) - except ValueError: - # no argument name given, only the type - parse_c_type(param, arg) + def get_index_text(self, modname, name): + """ + Return the text for the index entry of the object. + """ + raise NotImplementedError('must be implemented in subclasses') + + def add_target_and_index(self, name_cls, sig, signode): + modname = self.options.get('module', self.env.currmodule) + fullname = (modname and modname + '.' or '') + name_cls[0] + # note target + if fullname not in self.state.document.ids: + signode['names'].append(fullname) + signode['ids'].append(fullname) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + self.env.note_descref(fullname, self.desctype, self.lineno) + + indextext = self.get_index_text(modname, name_cls) + if indextext: + self.indexnode['entries'].append(('single', indextext, + fullname, fullname)) + + def before_content(self): + # needed for automatic qualification of members (reset in subclasses) + self.clsname_set = False + + def after_content(self): + if self.clsname_set: + self.env.currclass = None + + +class ModulelevelDesc(PythonDesc): + """ + Description of an object on module level (functions, data). + """ + + def needs_arglist(self): + return self.desctype == 'function' + + def get_index_text(self, modname, name_cls): + if self.desctype == 'function': + if not modname: + return _('%s() (built-in function)') % name_cls[0] + return _('%s() (in module %s)') % (name_cls[0], modname) + elif self.desctype == 'data': + if not modname: + return _('%s (built-in variable)') % name_cls[0] + return _('%s (in module %s)') % (name_cls[0], modname) else: - parse_c_type(param, ctype) - param += nodes.emphasis(' '+argname, ' '+argname) - paramlist += param - signode += paramlist - if const: - signode += addnodes.desc_addname(const, const) - return name + return '' -option_desc_re = re.compile( - r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)') +class ClasslikeDesc(PythonDesc): + """ + Description of a class-like object (classes, interfaces, exceptions). + """ -def parse_option_desc(signode, sig): - """Transform an option description into RST nodes.""" - count = 0 - firstname = '' - for m in option_desc_re.finditer(sig): - optname, args = m.groups() - if count: - signode += addnodes.desc_addname(', ', ', ') - signode += addnodes.desc_name(optname, optname) - signode += addnodes.desc_addname(args, args) - if not count: - firstname = optname - count += 1 - if not firstname: - raise ValueError - return firstname + def get_signature_prefix(self, sig): + return self.desctype + ' ' + def get_index_text(self, modname, name_cls): + if self.desctype == 'class': + if not modname: + return _('%s (built-in class)') % name_cls[0] + return _('%s (class in %s)') % (name_cls[0], modname) + elif self.desctype == 'exception': + return name_cls[0] + else: + return '' -strip_backslash_re = re.compile(r'\\(?=[^\\])') + def before_content(self): + PythonDesc.before_content(self) + if self.names: + self.env.currclass = self.names[0][0] + self.clsname_set = True -def desc_directive(desctype, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - inode = addnodes.index(entries=[]) - node = addnodes.desc() - node['desctype'] = desctype - - noindex = ('noindex' in options) - node['noindex'] = noindex - # remove backslashes to support (dummy) escapes; helps Vim's highlighting - signatures = map(lambda s: strip_backslash_re.sub('', s.strip()), - arguments[0].split('\n')) - names = [] - clsname = None - module = options.get('module', env.currmodule) - for i, sig in enumerate(signatures): - # add a signature node for each signature in the current unit - # and add a reference target for it - sig = sig.strip() - signode = addnodes.desc_signature(sig, '') - signode['first'] = False - node.append(signode) - try: - if desctype in ('function', 'data', 'class', 'exception', - 'method', 'staticmethod', 'attribute'): - name, clsname = parse_py_signature(signode, sig, desctype, module, env) - elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'): - name = parse_c_signature(signode, sig, desctype) - elif desctype == 'cmdoption': - optname = parse_option_desc(signode, sig) - if not noindex: - targetname = optname.replace('/', '-') - if env.currprogram: - targetname = '-' + env.currprogram + targetname - targetname = 'cmdoption' + targetname - signode['ids'].append(targetname) - state.document.note_explicit_target(signode) - inode['entries'].append( - ('pair', _('%scommand line option; %s') % - ((env.currprogram and env.currprogram + ' ' or ''), sig), - targetname, targetname)) - env.note_progoption(optname, targetname) - continue - elif desctype == 'describe': - signode.clear() - signode += addnodes.desc_name(sig, sig) - continue + +class ClassmemberDesc(PythonDesc): + """ + Description of a class member (methods, attributes). + """ + + def needs_arglist(self): + return self.desctype.endswith('method') + + def get_signature_prefix(self, sig): + if self.desctype == 'staticmethod': + return 'static ' + elif self.desctype == 'classmethod': + return 'classmethod ' + return '' + + def get_index_text(self, modname, name_cls): + name, cls = name_cls + add_modules = self.env.config.add_module_names + if self.desctype == 'method': + try: + clsname, methname = name.rsplit('.', 1) + except ValueError: + if modname: + return _('%s() (in module %s)') % (name, modname) + else: + return '%s()' % name + if modname and add_modules: + return _('%s() (%s.%s method)') % (methname, modname, clsname) else: - # another registered generic x-ref directive - rolename, indextemplate, parse_node = additional_xref_types[desctype] - if parse_node: - fullname = parse_node(env, sig, signode) + return _('%s() (%s method)') % (methname, clsname) + elif self.desctype == 'staticmethod': + try: + clsname, methname = name.rsplit('.', 1) + except ValueError: + if modname: + return _('%s() (in module %s)') % (name, modname) else: - signode.clear() - signode += addnodes.desc_name(sig, sig) - # normalize whitespace like xfileref_role does - fullname = ws_re.sub('', sig) - if not noindex: - targetname = '%s-%s' % (rolename, fullname) - signode['ids'].append(targetname) - state.document.note_explicit_target(signode) - if indextemplate: - indexentry = _(indextemplate) % (fullname,) - indextype = 'single' - colon = indexentry.find(':') - if colon != -1: - indextype = indexentry[:colon].strip() - indexentry = indexentry[colon+1:].strip() - inode['entries'].append((indextype, indexentry, - targetname, targetname)) - env.note_reftarget(rolename, fullname, targetname) - # don't use object indexing below - continue - except ValueError, err: - # signature parsing failed + return '%s()' % name + if modname and add_modules: + return _('%s() (%s.%s static method)') % (methname, modname, + clsname) + else: + return _('%s() (%s static method)') % (methname, clsname) + elif self.desctype == 'classmethod': + try: + clsname, methname = name.rsplit('.', 1) + except ValueError: + if modname: + return '%s() (in module %s)' % (name, modname) + else: + return '%s()' % name + if modname: + return '%s() (%s.%s class method)' % (methname, modname, + clsname) + else: + return '%s() (%s class method)' % (methname, clsname) + elif self.desctype == 'attribute': + try: + clsname, attrname = name.rsplit('.', 1) + except ValueError: + if modname: + return _('%s (in module %s)') % (name, modname) + else: + return name + if modname and add_modules: + return _('%s (%s.%s attribute)') % (attrname, modname, clsname) + else: + return _('%s (%s attribute)') % (attrname, clsname) + else: + return '' + + def before_content(self): + PythonDesc.before_content(self) + if self.names and self.names[-1][1] and not self.env.currclass: + self.env.currclass = self.names[-1][1].strip('.') + self.clsname_set = True + + +class CDesc(DescDirective): + """ + Description of a C language object. + """ + + # These C types aren't described anywhere, so don't try to create + # a cross-reference to them + stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct')) + + def _parse_type(self, node, ctype): + # add cross-ref nodes for all words + for part in filter(None, wsplit_re.split(ctype)): + tnode = nodes.Text(part, part) + if part[0] in string.ascii_letters+'_' and \ + part not in self.stopwords: + pnode = addnodes.pending_xref( + '', reftype='ctype', reftarget=part, + modname=None, classname=None) + pnode += tnode + node += pnode + else: + node += tnode + + def parse_signature(self, sig, signode): + """Transform a C (or C++) signature into RST nodes.""" + # first try the function pointer signature regex, it's more specific + m = c_funcptr_sig_re.match(sig) + if m is None: + m = c_sig_re.match(sig) + if m is None: + raise ValueError('no match') + rettype, name, arglist, const = m.groups() + + signode += addnodes.desc_type('', '') + self._parse_type(signode[-1], rettype) + try: + classname, funcname = name.split('::', 1) + classname += '::' + signode += addnodes.desc_addname(classname, classname) + signode += addnodes.desc_name(funcname, funcname) + # name (the full name) is still both parts + except ValueError: + signode += addnodes.desc_name(name, name) + # clean up parentheses from canonical name + m = c_funcptr_name_re.match(name) + if m: + name = m.group(1) + if not arglist: + if self.desctype == 'cfunction': + # for functions, add an empty parameter list + signode += addnodes.desc_parameterlist() + return name + + paramlist = addnodes.desc_parameterlist() + arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup + # this messes up function pointer types, but not too badly ;) + args = arglist.split(',') + for arg in args: + arg = arg.strip() + param = addnodes.desc_parameter('', '', noemph=True) + try: + ctype, argname = arg.rsplit(' ', 1) + except ValueError: + # no argument name given, only the type + self._parse_type(param, arg) + else: + self._parse_type(param, ctype) + param += nodes.emphasis(' '+argname, ' '+argname) + paramlist += param + signode += paramlist + if const: + signode += addnodes.desc_addname(const, const) + return name + + def get_index_text(self, name): + if self.desctype == 'cfunction': + return _('%s (C function)') % name + elif self.desctype == 'cmember': + return _('%s (C member)') % name + elif self.desctype == 'cmacro': + return _('%s (C macro)') % name + elif self.desctype == 'ctype': + return _('%s (C type)') % name + elif self.desctype == 'cvar': + return _('%s (C variable)') % name + else: + return '' + + def add_target_and_index(self, name, sig, signode): + # note target + if name not in self.state.document.ids: + signode['names'].append(name) + signode['ids'].append(name) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + self.env.note_descref(name, self.desctype, self.lineno) + + indextext = self.get_index_text(name) + if indextext: + self.indexnode['entries'].append(('single', indextext, name, name)) + + +class CmdoptionDesc(DescDirective): + """ + Description of a command-line option (.. cmdoption). + """ + + def parse_signature(self, sig, signode): + """Transform an option description into RST nodes.""" + count = 0 + firstname = '' + for m in option_desc_re.finditer(sig): + optname, args = m.groups() + if count: + signode += addnodes.desc_addname(', ', ', ') + signode += addnodes.desc_name(optname, optname) + signode += addnodes.desc_addname(args, args) + if not count: + firstname = optname + count += 1 + if not firstname: + raise ValueError + return firstname + + def add_target_and_index(self, name, sig, signode): + targetname = name.replace('/', '-') + if self.env.currprogram: + targetname = '-' + self.env.currprogram + targetname + targetname = 'cmdoption' + targetname + signode['ids'].append(targetname) + self.state.document.note_explicit_target(signode) + self.indexnode['entries'].append( + ('pair', _('%scommand line option; %s') % + ((self.env.currprogram and + self.env.currprogram + ' ' or ''), sig), + targetname, targetname)) + self.env.note_progoption(name, targetname) + + +class GenericDesc(DescDirective): + """ + A generic x-ref directive registered with Sphinx.add_description_unit(). + """ + + def parse_signature(self, sig, signode): + parse_node = additional_xref_types[self.desctype][2] + if parse_node: + name = parse_node(self.env, sig, signode) + else: signode.clear() signode += addnodes.desc_name(sig, sig) - continue # we don't want an index entry here - # only add target and index entry if this is the first description of the - # function name in this desc block - if not noindex and name not in names: - fullname = (module and module + '.' or '') + name - # note target - if fullname not in state.document.ids: - signode['names'].append(fullname) - signode['ids'].append(fullname) - signode['first'] = (not names) - state.document.note_explicit_target(signode) - env.note_descref(fullname, desctype, lineno) - names.append(name) - - indextext = desc_index_text(desctype, module, name, - env.config.add_module_names) - inode['entries'].append(('single', indextext, fullname, fullname)) - - subnode = addnodes.desc_content() - # needed for automatic qualification of members - clsname_set = False - if desctype in ('class', 'exception') and names: - env.currclass = names[0] - clsname_set = True - elif desctype in ('method', 'staticmethod', 'attribute') and \ - clsname and not env.currclass: - env.currclass = clsname.strip('.') - clsname_set = True - # needed for association of version{added,changed} directives - if names: - env.currdesc = names[0] - state.nested_parse(content, content_offset, subnode) - handle_doc_fields(subnode, env) - if clsname_set: - env.currclass = None - env.currdesc = None - node.append(subnode) - return [inode, node] - -desc_directive.content = 1 -desc_directive.arguments = (1, 0, 1) -desc_directive.options = {'noindex': directives.flag, - 'module': directives.unchanged} - -desctypes = [ - # the Python ones - 'function', - 'data', - 'class', - 'method', - 'staticmethod', - 'attribute', - 'exception', - # the C ones - 'cfunction', - 'cmember', - 'cmacro', - 'ctype', - 'cvar', - # for command line options - 'cmdoption', - # the generic one - 'describe', - 'envvar', -] - -for _name in desctypes: - directives.register_directive(_name, desc_directive) + # normalize whitespace like xfileref_role does + name = ws_re.sub('', sig) + return name + + def add_target_and_index(self, name, sig, signode): + rolename, indextemplate = additional_xref_types[self.desctype][:2] + targetname = '%s-%s' % (rolename, name) + signode['ids'].append(targetname) + self.state.document.note_explicit_target(signode) + if indextemplate: + indexentry = _(indextemplate) % (name,) + indextype = 'single' + colon = indexentry.find(':') + if colon != -1: + indextype = indexentry[:colon].strip() + indexentry = indexentry[colon+1:].strip() + self.indexnode['entries'].append((indextype, indexentry, + targetname, targetname)) + self.env.note_reftarget(rolename, name, targetname) + + +class Target(Directive): + """ + Generic target for user-defined cross-reference types. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + rolename, indextemplate, foo = additional_xref_types[self.name] + # normalize whitespace in fullname like xfileref_role does + fullname = ws_re.sub('', self.arguments[0].strip()) + targetname = '%s-%s' % (rolename, fullname) + node = nodes.target('', '', ids=[targetname]) + self.state.document.note_explicit_target(node) + ret = [node] + if indextemplate: + indexentry = indextemplate % (fullname,) + indextype = 'single' + colon = indexentry.find(':') + if colon != -1: + indextype = indexentry[:colon].strip() + indexentry = indexentry[colon+1:].strip() + inode = addnodes.index(entries=[(indextype, indexentry, + targetname, targetname)]) + ret.insert(0, inode) + env.note_reftarget(rolename, fullname, targetname) + return ret + +# Note: the target directive is not registered here, it is used by the +# application when registering additional xref types. _ = lambda x: x # Generic cross-reference types; they can be registered in the application; -# the directives are either desc_directive or target_directive +# the directives are either desc_directive or target_directive. additional_xref_types = { # directive name: (role name, index text, function to parse the desc node) 'envvar': ('envvar', _('environment variable; %s'), None), @@ -560,33 +748,22 @@ additional_xref_types = { del _ -# ------ target -------------------------------------------------------------------- - -def target_directive(targettype, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - """Generic target for user-defined cross-reference types.""" - env = state.document.settings.env - rolename, indextemplate, foo = additional_xref_types[targettype] - # normalize whitespace in fullname like xfileref_role does - fullname = ws_re.sub('', arguments[0].strip()) - targetname = '%s-%s' % (rolename, fullname) - node = nodes.target('', '', ids=[targetname]) - state.document.note_explicit_target(node) - ret = [node] - if indextemplate: - indexentry = indextemplate % (fullname,) - indextype = 'single' - colon = indexentry.find(':') - if colon != -1: - indextype = indexentry[:colon].strip() - indexentry = indexentry[colon+1:].strip() - inode = addnodes.index(entries=[(indextype, indexentry, targetname, targetname)]) - ret.insert(0, inode) - env.note_reftarget(rolename, fullname, targetname) - return ret - -target_directive.content = 0 -target_directive.arguments = (1, 0, 1) - -# note, the target directive is not registered here, it is used by the application -# when registering additional xref types +directives.register_directive('describe', directive_dwim(DescDirective)) + +directives.register_directive('function', directive_dwim(ModulelevelDesc)) +directives.register_directive('data', directive_dwim(ModulelevelDesc)) +directives.register_directive('class', directive_dwim(ClasslikeDesc)) +directives.register_directive('exception', directive_dwim(ClasslikeDesc)) +directives.register_directive('method', directive_dwim(ClassmemberDesc)) +directives.register_directive('classmethod', directive_dwim(ClassmemberDesc)) +directives.register_directive('staticmethod', directive_dwim(ClassmemberDesc)) +directives.register_directive('attribute', directive_dwim(ClassmemberDesc)) + +directives.register_directive('cfunction', directive_dwim(CDesc)) +directives.register_directive('cmember', directive_dwim(CDesc)) +directives.register_directive('cmacro', directive_dwim(CDesc)) +directives.register_directive('ctype', directive_dwim(CDesc)) +directives.register_directive('cvar', directive_dwim(CDesc)) + +directives.register_directive('cmdoption', directive_dwim(CmdoptionDesc)) +directives.register_directive('envvar', directive_dwim(GenericDesc)) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index f92683a3..80c88f5f 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -8,260 +8,324 @@ """ import re -import posixpath from docutils import nodes from docutils.parsers.rst import directives from sphinx import addnodes from sphinx.locale import pairindextypes -from sphinx.util import patfilter, ws_re, caption_ref_re -from sphinx.util.compat import make_admonition - - -# ------ the TOC tree --------------------------------------------------------------- - -def toctree_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - suffix = env.config.source_suffix - dirname = posixpath.dirname(env.docname) - glob = 'glob' in options - - ret = [] - subnode = addnodes.toctree() - includefiles = [] - includetitles = {} - all_docnames = env.found_docs.copy() - # don't add the currently visited file in catch-all patterns - all_docnames.remove(env.docname) - for entry in content: - if not entry: - continue - if not glob: - # look for explicit titles and documents ("Some Title <document>"). - m = caption_ref_re.match(entry) - if m: - docname = m.group(2) - includetitles[docname] = m.group(1) - else: - docname = entry - # remove suffixes (backwards compatibility) - if docname.endswith(suffix): - docname = docname[:-len(suffix)] - # absolutize filenames - docname = posixpath.normpath(posixpath.join(dirname, docname)) - if docname not in env.found_docs: - ret.append(state.document.reporter.warning( - 'toctree references unknown document %r' % docname, line=lineno)) +from sphinx.util import patfilter, ws_re, caption_ref_re, url_re, docname_join +from sphinx.util.compat import Directive, directive_dwim, make_admonition + + +class TocTree(Directive): + """ + Directive to notify Sphinx about the hierarchical structure of the docs, + and to include a table-of-contents like tree in the current document. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'maxdepth': int, + 'glob': directives.flag, + 'hidden': directives.flag, + 'numbered': directives.flag, + } + + def run(self): + env = self.state.document.settings.env + suffix = env.config.source_suffix + glob = 'glob' in self.options + + ret = [] + # (title, ref) pairs, where ref may be a document, or an external link, + # and title may be None if the document's title is to be used + entries = [] + includefiles = [] + includetitles = {} + all_docnames = env.found_docs.copy() + # don't add the currently visited file in catch-all patterns + all_docnames.remove(env.docname) + for entry in self.content: + if not entry: + continue + if not glob: + # look for explicit titles ("Some Title <document>") + m = caption_ref_re.match(entry) + if m: + ref = m.group(2) + title = m.group(1) + docname = ref + else: + ref = docname = entry + title = None + # remove suffixes (backwards compatibility) + if docname.endswith(suffix): + docname = docname[:-len(suffix)] + # absolutize filenames + docname = docname_join(env.docname, docname) + if url_re.match(ref) or ref == 'self': + entries.append((title, ref)) + elif docname not in env.found_docs: + ret.append(self.state.document.reporter.warning( + 'toctree references unknown document %r' % docname, + line=self.lineno)) + else: + entries.append((title, docname)) + includefiles.append(docname) else: - includefiles.append(docname) - else: - patname = posixpath.normpath(posixpath.join(dirname, entry)) - docnames = sorted(patfilter(all_docnames, patname)) - for docname in docnames: - all_docnames.remove(docname) # don't include it again - includefiles.append(docname) - if not docnames: - ret.append(state.document.reporter.warning( - 'toctree glob pattern %r didn\'t match any documents' % entry, - line=lineno)) - subnode['includefiles'] = includefiles - subnode['includetitles'] = includetitles - subnode['maxdepth'] = options.get('maxdepth', -1) - subnode['glob'] = glob - ret.append(subnode) - return ret - -toctree_directive.content = 1 -toctree_directive.options = {'maxdepth': int, 'glob': directives.flag} -directives.register_directive('toctree', toctree_directive) - - -# ------ section metadata ---------------------------------------------------------- - -def module_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - modname = arguments[0].strip() - noindex = 'noindex' in options - env.currmodule = modname - env.note_module(modname, options.get('synopsis', ''), - options.get('platform', ''), - 'deprecated' in options) - modulenode = addnodes.module() - modulenode['modname'] = modname - modulenode['synopsis'] = options.get('synopsis', '') - targetnode = nodes.target('', '', ids=['module-' + modname]) - state.document.note_explicit_target(targetnode) - ret = [modulenode, targetnode] - if 'platform' in options: - modulenode['platform'] = options['platform'] - node = nodes.paragraph() - node += nodes.emphasis('', _('Platforms: ')) - node += nodes.Text(options['platform'], options['platform']) - ret.append(node) - # the synopsis isn't printed; in fact, it is only used in the modindex currently - if not noindex: - indextext = _('%s (module)') % modname - inode = addnodes.index(entries=[('single', indextext, - 'module-' + modname, modname)]) - ret.insert(0, inode) - return ret - -module_directive.arguments = (1, 0, 0) -module_directive.options = {'platform': lambda x: x, - 'synopsis': lambda x: x, - 'noindex': directives.flag, - 'deprecated': directives.flag} -directives.register_directive('module', module_directive) - - -def currentmodule_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # This directive is just to tell people that we're documenting - # stuff in module foo, but links to module foo won't lead here. - env = state.document.settings.env - modname = arguments[0].strip() - if modname == 'None': - env.currmodule = None - else: + patname = docname_join(env.docname, entry) + docnames = sorted(patfilter(all_docnames, patname)) + for docname in docnames: + all_docnames.remove(docname) # don't include it again + entries.append((None, docname)) + includefiles.append(docname) + if not docnames: + ret.append(self.state.document.reporter.warning( + 'toctree glob pattern %r didn\'t match any documents' + % entry, line=self.lineno)) + subnode = addnodes.toctree() + subnode['parent'] = env.docname + # entries contains all entries (self references, external links etc.) + subnode['entries'] = entries + # includefiles only entries that are documents + subnode['includefiles'] = includefiles + subnode['maxdepth'] = self.options.get('maxdepth', -1) + subnode['glob'] = glob + subnode['hidden'] = 'hidden' in self.options + subnode['numbered'] = 'numbered' in self.options + ret.append(subnode) + return ret + + +class Module(Directive): + """ + Directive to mark description of a new module. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'platform': lambda x: x, + 'synopsis': lambda x: x, + 'noindex': directives.flag, + 'deprecated': directives.flag, + } + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + noindex = 'noindex' in self.options env.currmodule = modname - return [] + env.note_module(modname, self.options.get('synopsis', ''), + self.options.get('platform', ''), + 'deprecated' in self.options) + modulenode = addnodes.module() + modulenode['modname'] = modname + modulenode['synopsis'] = self.options.get('synopsis', '') + targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) + self.state.document.note_explicit_target(targetnode) + ret = [modulenode, targetnode] + if 'platform' in self.options: + platform = self.options['platform'] + modulenode['platform'] = platform + node = nodes.paragraph() + node += nodes.emphasis('', _('Platforms: ')) + node += nodes.Text(platform, platform) + ret.append(node) + # the synopsis isn't printed; in fact, it is only used in the + # modindex currently + if not noindex: + indextext = _('%s (module)') % modname + inode = addnodes.index(entries=[('single', indextext, + 'module-' + modname, modname)]) + ret.insert(0, inode) + return ret + + +class CurrentModule(Directive): + """ + This directive is just to tell Sphinx that we're documenting + stuff in module foo, but links to module foo won't lead here. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + if modname == 'None': + env.currmodule = None + else: + env.currmodule = modname + return [] -currentmodule_directive.arguments = (1, 0, 0) -directives.register_directive('currentmodule', currentmodule_directive) +class Author(Directive): + """ + Directive to give the name of the author of the current document + or section. Shown in the output only if the show_authors option is on. + """ -def author_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # Show authors only if the show_authors option is on - env = state.document.settings.env - if not env.config.show_authors: - return [] - para = nodes.paragraph() - emph = nodes.emphasis() - para += emph - if name == 'sectionauthor': - text = _('Section author: ') - elif name == 'moduleauthor': - text = _('Module author: ') - else: - text = _('Author: ') - emph += nodes.Text(text, text) - inodes, messages = state.inline_text(arguments[0], lineno) - emph.extend(inodes) - return [para] + messages - -author_directive.arguments = (1, 0, 1) -directives.register_directive('sectionauthor', author_directive) -directives.register_directive('moduleauthor', author_directive) - - -def program_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - program = ws_re.sub('-', arguments[0].strip()) - if program == 'None': - env.currprogram = None - else: - env.currprogram = program - return [] - -program_directive.arguments = (1, 0, 1) -directives.register_directive('program', program_directive) - - -# ------ index markup -------------------------------------------------------------- - -indextypes = [ - 'single', 'pair', 'triple', -] - -def index_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - arguments = arguments[0].split('\n') - env = state.document.settings.env - targetid = 'index-%s' % env.index_num - env.index_num += 1 - targetnode = nodes.target('', '', ids=[targetid]) - state.document.note_explicit_target(targetnode) - indexnode = addnodes.index() - indexnode['entries'] = ne = [] - for entry in arguments: - entry = entry.strip() - for type in pairindextypes: - if entry.startswith(type+':'): - value = entry[len(type)+1:].strip() - value = pairindextypes[type] + '; ' + value - ne.append(('pair', value, targetid, value)) - break + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + if not env.config.show_authors: + return [] + para = nodes.paragraph() + emph = nodes.emphasis() + para += emph + if self.name == 'sectionauthor': + text = _('Section author: ') + elif self.name == 'moduleauthor': + text = _('Module author: ') else: - for type in indextypes: + text = _('Author: ') + emph += nodes.Text(text, text) + inodes, messages = self.state.inline_text(self.arguments[0], + self.lineno) + emph.extend(inodes) + return [para] + messages + + +class Program(Directive): + """ + Directive to name the program for which options are documented. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + program = ws_re.sub('-', self.arguments[0].strip()) + if program == 'None': + env.currprogram = None + else: + env.currprogram = program + return [] + + +class Index(Directive): + """ + Directive to add entries to the index. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + indextypes = [ + 'single', 'pair', 'triple', + ] + + def run(self): + arguments = self.arguments[0].split('\n') + env = self.state.document.settings.env + targetid = 'index-%s' % env.index_num + env.index_num += 1 + targetnode = nodes.target('', '', ids=[targetid]) + self.state.document.note_explicit_target(targetnode) + indexnode = addnodes.index() + indexnode['entries'] = ne = [] + for entry in arguments: + entry = entry.strip() + for type in pairindextypes: if entry.startswith(type+':'): value = entry[len(type)+1:].strip() - ne.append((type, value, targetid, value)) + value = pairindextypes[type] + '; ' + value + ne.append(('pair', value, targetid, value)) break - # shorthand notation for single entries else: - for value in entry.split(','): - value = value.strip() - if not value: - continue - ne.append(('single', value, targetid, value)) - return [indexnode, targetnode] - -index_directive.arguments = (1, 0, 1) -directives.register_directive('index', index_directive) - -# ------ versionadded/versionchanged ----------------------------------------------- - -def version_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - node = addnodes.versionmodified() - node['type'] = name - node['version'] = arguments[0] - if len(arguments) == 2: - inodes, messages = state.inline_text(arguments[1], lineno+1) - node.extend(inodes) - if content: - state.nested_parse(content, content_offset, node) - ret = [node] + messages - else: - ret = [node] - env = state.document.settings.env - env.note_versionchange(node['type'], node['version'], node, lineno) - return ret - -version_directive.arguments = (1, 1, 1) -version_directive.content = 1 - -directives.register_directive('deprecated', version_directive) -directives.register_directive('versionadded', version_directive) -directives.register_directive('versionchanged', version_directive) - - -# ------ see also ------------------------------------------------------------------ - -def seealso_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - ret = make_admonition( - addnodes.seealso, name, [_('See also')], options, content, - lineno, content_offset, block_text, state, state_machine) - if arguments: - argnodes, msgs = state.inline_text(arguments[0], lineno) - para = nodes.paragraph() - para += argnodes - para += msgs - ret[0].insert(1, para) - return ret - -seealso_directive.content = 1 -seealso_directive.arguments = (0, 1, 1) -directives.register_directive('seealso', seealso_directive) - + for type in self.indextypes: + if entry.startswith(type+':'): + value = entry[len(type)+1:].strip() + ne.append((type, value, targetid, value)) + break + # shorthand notation for single entries + else: + for value in entry.split(','): + value = value.strip() + if not value: + continue + ne.append(('single', value, targetid, value)) + return [indexnode, targetnode] + + +class VersionChange(Directive): + """ + Directive to describe a change/addition/deprecation in a specific version. + """ + + has_content = True + required_arguments = 1 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + def run(self): + node = addnodes.versionmodified() + node.document = self.state.document + node['type'] = self.name + node['version'] = self.arguments[0] + if len(self.arguments) == 2: + inodes, messages = self.state.inline_text(self.arguments[1], + self.lineno+1) + node.extend(inodes) + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) + ret = [node] + messages + else: + ret = [node] + env = self.state.document.settings.env + env.note_versionchange(node['type'], node['version'], node, self.lineno) + return ret + + +class SeeAlso(Directive): + """ + An admonition mentioning things to look at as reference. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + def run(self): + ret = make_admonition( + addnodes.seealso, self.name, [_('See also')], self.options, + self.content, self.lineno, self.content_offset, self.block_text, + self.state, self.state_machine) + if self.arguments: + argnodes, msgs = self.state.inline_text(self.arguments[0], + self.lineno) + para = nodes.paragraph() + para += argnodes + para += msgs + ret[0].insert(1, para) + return ret -# ------ production list (for the reference) --------------------------------------- token_re = re.compile('`([a-z_]+)`') @@ -284,116 +348,232 @@ def token_xrefs(text, env): retnodes.append(nodes.Text(text[pos:], text[pos:])) return retnodes -def productionlist_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - node = addnodes.productionlist() - messages = [] - i = 0 - - for rule in arguments[0].split('\n'): - if i == 0 and ':' not in rule: - # production group - continue - i += 1 - try: - name, tokens = rule.split(':', 1) - except ValueError: - break - subnode = addnodes.production() - subnode['tokenname'] = name.strip() - if subnode['tokenname']: - idname = 'grammar-token-%s' % subnode['tokenname'] - if idname not in state.document.ids: - subnode['ids'].append(idname) - state.document.note_implicit_target(subnode, subnode) - env.note_reftarget('token', subnode['tokenname'], idname) - subnode.extend(token_xrefs(tokens, env)) - node.append(subnode) - return [node] + messages - -productionlist_directive.content = 0 -productionlist_directive.arguments = (1, 0, 1) -directives.register_directive('productionlist', productionlist_directive) - - -# ------ glossary directive --------------------------------------------------------- - -def glossary_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - """Glossary with cross-reference targets for :term: roles.""" - env = state.document.settings.env - node = addnodes.glossary() - state.nested_parse(content, content_offset, node) - - # the content should be definition lists - dls = [child for child in node if isinstance(child, nodes.definition_list)] - # now, extract definition terms to enable cross-reference creation - for dl in dls: - dl['classes'].append('glossary') - for li in dl.children: - if not li.children or not isinstance(li[0], nodes.term): +class ProductionList(Directive): + """ + Directive to list grammar productions. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + node = addnodes.productionlist() + messages = [] + i = 0 + + for rule in self.arguments[0].split('\n'): + if i == 0 and ':' not in rule: + # production group continue - termtext = li.children[0].astext() - new_id = 'term-' + nodes.make_id(termtext) - if new_id in env.gloss_entries: - new_id = 'term-' + str(len(env.gloss_entries)) - env.gloss_entries.add(new_id) - li[0]['names'].append(new_id) - li[0]['ids'].append(new_id) - state.document.settings.env.note_reftarget('term', termtext.lower(), - new_id) - # add an index entry too - indexnode = addnodes.index() - indexnode['entries'] = [('single', termtext, new_id, termtext)] - li.insert(0, indexnode) - return [node] - -glossary_directive.content = 1 -glossary_directive.arguments = (0, 0, 0) -directives.register_directive('glossary', glossary_directive) - - -# ------ miscellaneous markup ------------------------------------------------------- - -def centered_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - if not arguments: - return [] - subnode = addnodes.centered() - inodes, messages = state.inline_text(arguments[0], lineno) - subnode.extend(inodes) - return [subnode] + messages - -centered_directive.arguments = (1, 0, 1) -directives.register_directive('centered', centered_directive) - - -def acks_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - node = addnodes.acks() - state.nested_parse(content, content_offset, node) - if len(node.children) != 1 or not isinstance(node.children[0], nodes.bullet_list): - return [state.document.reporter.warning('.. acks content is not a list', - line=lineno)] - return [node] - -acks_directive.content = 1 -acks_directive.arguments = (0, 0, 0) -directives.register_directive('acks', acks_directive) - - -def tabularcolumns_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # support giving explicit tabulary column definition to latex - node = addnodes.tabular_col_spec() - node['spec'] = arguments[0] - return [node] - -tabularcolumns_directive.content = 0 -tabularcolumns_directive.arguments = (1, 0, 1) -directives.register_directive('tabularcolumns', tabularcolumns_directive) - + i += 1 + try: + name, tokens = rule.split(':', 1) + except ValueError: + break + subnode = addnodes.production() + subnode['tokenname'] = name.strip() + if subnode['tokenname']: + idname = 'grammar-token-%s' % subnode['tokenname'] + if idname not in self.state.document.ids: + subnode['ids'].append(idname) + self.state.document.note_implicit_target(subnode, subnode) + env.note_reftarget('token', subnode['tokenname'], idname) + subnode.extend(token_xrefs(tokens, env)) + node.append(subnode) + return [node] + messages + + +class TabularColumns(Directive): + """ + Directive to give an explicit tabulary column definition to LaTeX. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + node = addnodes.tabular_col_spec() + node['spec'] = self.arguments[0] + return [node] + + +class Glossary(Directive): + """ + Directive to create a glossary with cross-reference targets + for :term: roles. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'sorted': directives.flag, + } + + def run(self): + env = self.state.document.settings.env + node = addnodes.glossary() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + + # the content should be definition lists + dls = [child for child in node + if isinstance(child, nodes.definition_list)] + # now, extract definition terms to enable cross-reference creation + new_dl = nodes.definition_list() + new_dl['classes'].append('glossary') + items = [] + for dl in dls: + for li in dl.children: + if not li.children or not isinstance(li[0], nodes.term): + continue + termtext = li.children[0].astext() + new_id = 'term-' + nodes.make_id(termtext) + if new_id in env.gloss_entries: + new_id = 'term-' + str(len(env.gloss_entries)) + env.gloss_entries.add(new_id) + li[0]['names'].append(new_id) + li[0]['ids'].append(new_id) + env.note_reftarget('term', termtext.lower(), new_id) + # add an index entry too + indexnode = addnodes.index() + indexnode['entries'] = [('single', termtext, new_id, termtext)] + li.insert(0, indexnode) + items.append((termtext, li)) + if 'sorted' in self.options: + items.sort() + new_dl.extend(item[1] for item in items) + node.children = [new_dl] + return [node] + + +class Centered(Directive): + """ + Directive to create a centered line of bold text. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + if not self.arguments: + return [] + subnode = addnodes.centered() + inodes, messages = self.state.inline_text(self.arguments[0], + self.lineno) + subnode.extend(inodes) + return [subnode] + messages + + + +class Acks(Directive): + """ + Directive for a list of names. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + node = addnodes.acks() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + if len(node.children) != 1 or not isinstance(node.children[0], + nodes.bullet_list): + return [self.state.document.reporter.warning( + '.. acks content is not a list', line=self.lineno)] + return [node] + + +class HList(Directive): + """ + Directive for a list that gets compacted horizontally. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'columns': int, + } + + def run(self): + ncolumns = self.options.get('columns', 2) + node = nodes.paragraph() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + if len(node.children) != 1 or not isinstance(node.children[0], + nodes.bullet_list): + return [self.state.document.reporter.warning( + '.. hlist content is not a list', line=self.lineno)] + fulllist = node.children[0] + # create a hlist node where the items are distributed + npercol, nmore = divmod(len(fulllist), ncolumns) + index = 0 + newnode = addnodes.hlist() + for column in range(ncolumns): + endindex = index + (column < nmore and (npercol+1) or npercol) + col = addnodes.hlistcol() + col += nodes.bullet_list() + col[0] += fulllist.children[index:endindex] + index = endindex + newnode += col + return [newnode] + + +class Only(Directive): + """ + Directive to only include text if the given tag(s) are enabled. + """ + + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + node = addnodes.only() + node.document = self.state.document + node.line = self.lineno + node['expr'] = self.arguments[0] + self.state.nested_parse(self.content, self.content_offset, node) + return [node] + + +directives.register_directive('toctree', directive_dwim(TocTree)) +directives.register_directive('module', directive_dwim(Module)) +directives.register_directive('currentmodule', directive_dwim(CurrentModule)) +directives.register_directive('sectionauthor', directive_dwim(Author)) +directives.register_directive('moduleauthor', directive_dwim(Author)) +directives.register_directive('program', directive_dwim(Program)) +directives.register_directive('index', directive_dwim(Index)) +directives.register_directive('deprecated', directive_dwim(VersionChange)) +directives.register_directive('versionadded', directive_dwim(VersionChange)) +directives.register_directive('versionchanged', directive_dwim(VersionChange)) +directives.register_directive('seealso', directive_dwim(SeeAlso)) +directives.register_directive('productionlist', directive_dwim(ProductionList)) +directives.register_directive('tabularcolumns', directive_dwim(TabularColumns)) +directives.register_directive('glossary', directive_dwim(Glossary)) +directives.register_directive('centered', directive_dwim(Centered)) +directives.register_directive('acks', directive_dwim(Acks)) +directives.register_directive('hlist', directive_dwim(HList)) +directives.register_directive('only', directive_dwim(Only)) # register the standard rst class directive under a different name diff --git a/sphinx/environment.py b/sphinx/environment.py index 2a9cf301..0884ecca 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -16,6 +16,7 @@ import heapq import types import codecs import imghdr +import string import difflib import cPickle as pickle from os import path @@ -23,12 +24,10 @@ from glob import glob from string import ascii_uppercase as uppercase from itertools import izip, groupby try: - import hashlib - md5 = hashlib.md5 + from hashlib import md5 except ImportError: # 2.4 compatibility - import md5 - md5 = md5.new + from md5 import md5 from docutils import nodes from docutils.io import FileInput, NullOutput @@ -43,9 +42,10 @@ from docutils.transforms import Transform from docutils.transforms.parts import ContentsFilter from sphinx import addnodes -from sphinx.util import movefile, get_matching_docs, SEP, ustrftime +from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \ + docname_join, FilenameUniqDict, url_re +from sphinx.errors import SphinxError from sphinx.directives import additional_xref_types -from sphinx.application import SphinxError default_settings = { 'embed_stylesheet': False, @@ -59,7 +59,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 26 +ENV_VERSION = 29 default_substitutions = set([ @@ -71,12 +71,12 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) -class RedirStream(object): - def __init__(self, writefunc): - self.writefunc = writefunc +class WarningStream(object): + def __init__(self, warnfunc): + self.warnfunc = warnfunc def write(self, text): if text.strip(): - self.writefunc(text) + self.warnfunc(text, None, '') class NoUri(Exception): @@ -116,7 +116,8 @@ class MoveModuleTargets(Transform): if not node['ids']: continue if node['ids'][0].startswith('module-') and \ - node.parent.__class__ is nodes.section: + node.parent.__class__ is nodes.section and \ + node.has_key('ismod'): node.parent['ids'] = node['ids'] node.parent.remove(node) @@ -133,6 +134,19 @@ class HandleCodeBlocks(Transform): nodes.doctest_block): node.replace_self(node.children[0]) + +class SortIds(Transform): + """ + Sort secion IDs so that the "id[0-9]+" one comes last. + """ + default_priority = 261 + + def apply(self): + for node in self.document.traverse(nodes.section): + if len(node['ids']) > 1 and node['ids'][0].startswith('id'): + node['ids'] = node['ids'][1:] + [node['ids'][0]] + + class CitationReferences(Transform): """ Handle citation references before the default docutils transform does. @@ -153,7 +167,7 @@ class SphinxStandaloneReader(standalone.Reader): Add our own transforms. """ transforms = [CitationReferences, DefaultSubstitutions, MoveModuleTargets, - HandleCodeBlocks] + HandleCodeBlocks, SortIds] def get_transforms(self): return standalone.Reader.get_transforms(self) + self.transforms @@ -166,7 +180,6 @@ class SphinxDummyWriter(UnfilteredWriter): pass - class SphinxContentsFilter(ContentsFilter): """ Used with BuildEnvironment.add_toc_from() to discard cross-file links @@ -205,8 +218,8 @@ class BuildEnvironment: self.set_warnfunc(None) values = self.config.values del self.config.values - # first write to a temporary file, so that if dumping fails, the existing - # environment won't be overwritten + # first write to a temporary file, so that if dumping fails, + # the existing environment won't be overwritten picklefile = open(filename + '.tmp', 'wb') # remove potentially pickling-problematic values from config for key, val in vars(self.config).items(): @@ -244,13 +257,14 @@ class BuildEnvironment: # this is to invalidate old pickles self.version = ENV_VERSION - # All "docnames" here are /-separated and relative and exclude the source suffix. + # All "docnames" here are /-separated and relative and exclude + # the source suffix. self.found_docs = set() # contains all existing docnames self.all_docs = {} # docname -> mtime at the time of build # contains all built docnames - self.dependencies = {} # docname -> set of dependent file names, relative to - # documentation root + self.dependencies = {} # docname -> set of dependent file + # names, relative to documentation root # File metadata self.metadata = {} # docname -> dict of metadata items @@ -259,29 +273,36 @@ class BuildEnvironment: self.titles = {} # docname -> title node self.tocs = {} # docname -> table of contents nodetree self.toc_num_entries = {} # docname -> number of real entries - # used to determine when to show the TOC in a sidebar - # (don't show if it's only one item) + # used to determine when to show the TOC + # in a sidebar (don't show if it's only one item) + self.toc_secnumbers = {} # docname -> dict of sectionid -> number + self.toctree_includes = {} # docname -> list of toctree includefiles - self.files_to_rebuild = {} # docname -> set of files (containing its TOCs) - # to rebuild too + self.files_to_rebuild = {} # docname -> set of files + # (containing its TOCs) to rebuild too self.glob_toctrees = set() # docnames that have :glob: toctrees + self.numbered_toctrees = set() # docnames that have :numbered: toctrees # X-ref target inventory self.descrefs = {} # fullname -> docname, desctype self.filemodules = {} # docname -> [modules] - self.modules = {} # modname -> docname, synopsis, platform, deprecated + self.modules = {} # modname -> docname, synopsis, + # platform, deprecated self.labels = {} # labelname -> docname, labelid, sectionname self.anonlabels = {} # labelname -> docname, labelid self.progoptions = {} # (program, name) -> docname, labelid self.reftargets = {} # (type, name) -> docname, labelid - # where type is term, token, envvar, citation + # type: term, token, envvar, citation # Other inventories self.indexentries = {} # docname -> list of # (type, string, target, aliasname) - self.versionchanges = {} # version -> list of - # (type, docname, lineno, module, descname, content) - self.images = {} # absolute path -> (docnames, unique filename) + self.versionchanges = {} # version -> list of (type, docname, + # lineno, module, descname, content) + + # these map absolute path -> (docnames, unique filename) + self.images = FilenameUniqDict() + self.dlfiles = FilenameUniqDict() # These are set while parsing a file self.docname = None # current document name @@ -293,21 +314,24 @@ class BuildEnvironment: self.gloss_entries = set() # existing definition labels # Some magically present labels - self.labels['genindex'] = ('genindex', '', _('Index')) - self.labels['modindex'] = ('modindex', '', _('Module Index')) - self.labels['search'] = ('search', '', _('Search Page')) + def add_magic_label(name, description): + self.labels[name] = (name, '', description) + self.anonlabels[name] = (name, '') + add_magic_label('genindex', _('Index')) + add_magic_label('modindex', _('Module Index')) + add_magic_label('search', _('Search Page')) def set_warnfunc(self, func): self._warnfunc = func - self.settings['warning_stream'] = RedirStream(func) + self.settings['warning_stream'] = WarningStream(func) def warn(self, docname, msg, lineno=None): if docname: if lineno is None: lineno = '' - self._warnfunc('%s:%s: %s' % (self.doc2path(docname), lineno, msg)) + self._warnfunc(msg, '%s:%s' % (self.doc2path(docname), lineno)) else: - self._warnfunc('GLOBAL:: ' + msg) + self._warnfunc(msg) def clear_doc(self, docname): """Remove all traces of a source file in the inventory.""" @@ -317,11 +341,15 @@ class BuildEnvironment: self.dependencies.pop(docname, None) self.titles.pop(docname, None) self.tocs.pop(docname, None) + self.toc_secnumbers.pop(docname, None) self.toc_num_entries.pop(docname, None) self.toctree_includes.pop(docname, None) self.filemodules.pop(docname, None) self.indexentries.pop(docname, None) self.glob_toctrees.discard(docname) + self.numbered_toctrees.discard(docname) + self.images.purge_doc(docname) + self.dlfiles.purge_doc(docname) for subfn, fnset in self.files_to_rebuild.items(): fnset.discard(docname) @@ -345,10 +373,6 @@ class BuildEnvironment: for version, changes in self.versionchanges.items(): new = [change for change in changes if change[1] != docname] changes[:] = new - for fullpath, (docs, _) in self.images.items(): - docs.discard(docname) - if not docs: - del self.images[fullpath] def doc2path(self, docname, base=True, suffix=None): """ @@ -360,7 +384,8 @@ class BuildEnvironment: """ suffix = suffix or self.config.source_suffix if base is True: - return path.join(self.srcdir, docname.replace(SEP, path.sep)) + suffix + return path.join(self.srcdir, + docname.replace(SEP, path.sep)) + suffix elif base is None: return docname.replace(SEP, path.sep) + suffix else: @@ -373,8 +398,10 @@ class BuildEnvironment: exclude_dirs = [d.replace(SEP, path.sep) for d in config.exclude_dirs] exclude_trees = [d.replace(SEP, path.sep) for d in config.exclude_trees] self.found_docs = set(get_matching_docs( - self.srcdir, config.source_suffix, exclude_docs=set(config.unused_docs), - exclude_dirs=exclude_dirs, exclude_trees=exclude_trees, + self.srcdir, config.source_suffix, + exclude_docs=set(config.unused_docs), + exclude_dirs=exclude_dirs, + exclude_trees=exclude_trees, exclude_dirnames=['_sources'] + config.exclude_dirnames)) def get_outdated_files(self, config_changed): @@ -426,18 +453,22 @@ class BuildEnvironment: return added, changed, removed def update(self, config, srcdir, doctreedir, app=None): - """(Re-)read all files new or changed since last update. Yields a summary - and then docnames as it processes them. Store all environment docnames - in the canonical format (ie using SEP as a separator in place of - os.path.sep).""" + """ + (Re-)read all files new or changed since last update. Returns a + summary, the total count of documents to reread and an iterator that + yields docnames as it processes them. Store all environment docnames in + the canonical format (ie using SEP as a separator in place of + os.path.sep). + """ config_changed = False if self.config is None: msg = '[new config] ' config_changed = True else: - # check if a config value was changed that affects how doctrees are read - for key, descr in config.config_values.iteritems(): - if not descr[1]: + # check if a config value was changed that affects how + # doctrees are read + for key, descr in config.values.iteritems(): + if descr[1] != 'env': continue if self.config[key] != config[key]: msg = '[config changed] ' @@ -454,6 +485,7 @@ class BuildEnvironment: self.srcdir = srcdir self.doctreedir = doctreedir self.find_files(config) + self.config = config added, changed, removed = self.get_outdated_files(config_changed) @@ -464,36 +496,37 @@ class BuildEnvironment: msg += '%s added, %s changed, %s removed' % (len(added), len(changed), len(removed)) - yield msg - self.config = config - self.app = app + def update_generator(): + self.app = app - # clear all files no longer present - for docname in removed: - if app: - app.emit('env-purge-doc', self, docname) - self.clear_doc(docname) - - # read all new and changed files - for docname in sorted(added | changed): - yield docname - self.read_doc(docname, app=app) + # clear all files no longer present + for docname in removed: + if app: + app.emit('env-purge-doc', self, docname) + self.clear_doc(docname) - if config.master_doc not in self.all_docs: - self.warn(None, 'master file %s not found' % - self.doc2path(config.master_doc)) + # read all new and changed files + to_read = added | changed + for docname in sorted(to_read): + yield docname + self.read_doc(docname, app=app) - self.app = None + if config.master_doc not in self.all_docs: + self.warn(None, 'master file %s not found' % + self.doc2path(config.master_doc)) - # remove all non-existing images from inventory - for imgsrc in self.images.keys(): - if not os.access(path.join(self.srcdir, imgsrc), os.R_OK): - del self.images[imgsrc] + self.app = None + if app: + app.emit('env-updated', self) - if app: - app.emit('env-updated', self) + return msg, len(added | changed), update_generator() + def check_dependents(self, already): + to_rewrite = self.assign_section_numbers() + for docname in to_rewrite: + if docname not in already: + yield docname # --------- SINGLE FILE READING -------------------------------------------- @@ -536,6 +569,10 @@ class BuildEnvironment: self.docname = docname self.settings['input_encoding'] = self.config.source_encoding + self.settings['trim_footnote_reference_space'] = \ + self.config.trim_footnote_reference_space + + codecs.register_error('sphinx', self.warn_and_replace) codecs.register_error('sphinx', self.warn_and_replace) @@ -543,13 +580,16 @@ class BuildEnvironment: def decode(self_, data): return data.decode(self_.encoding, 'sphinx') - def read(self): - data = FileInput.read(self) + def read(self_): + data = FileInput.read(self_) if app: arg = [data] app.emit('source-read', docname, arg) data = arg[0] - return data + if self.config.rst_epilog: + return data + '\n' + self.config.rst_epilog + '\n' + else: + return data # publish manually pub = Publisher(reader=SphinxStandaloneReader(), @@ -564,10 +604,12 @@ class BuildEnvironment: pub.publish() doctree = pub.document except UnicodeError, err: - raise SphinxError(err.message) + import pdb; pdb.set_trace() + raise SphinxError(str(err)) self.filter_messages(doctree) self.process_dependencies(docname, doctree) self.process_images(docname, doctree) + self.process_downloads(docname, doctree) self.process_metadata(docname, doctree) self.create_title_from(docname, doctree) self.note_labels_from(docname, doctree) @@ -599,7 +641,8 @@ class BuildEnvironment: if save_parsed: # save the parsed doctree - doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree') + doctree_filename = self.doc2path(docname, self.doctreedir, + '.doctree') dirname = path.dirname(doctree_filename) if not path.isdir(dirname): os.makedirs(dirname) @@ -636,11 +679,30 @@ class BuildEnvironment: path.normpath(path.join(cwd, dep))) self.dependencies.setdefault(docname, set()).add(relpath) + def process_downloads(self, docname, doctree): + """ + Process downloadable file paths. + """ + docdir = path.dirname(self.doc2path(docname, base=None)) + for node in doctree.traverse(addnodes.download_reference): + targetname = node['reftarget'] + if targetname.startswith('/') or targetname.startswith(os.sep): + # absolute + filepath = targetname[1:] + else: + filepath = path.normpath(path.join(docdir, node['reftarget'])) + self.dependencies.setdefault(docname, set()).add(filepath) + if not os.access(path.join(self.srcdir, filepath), os.R_OK): + self.warn(docname, 'download file not readable: %s' % filepath, + getattr(node, 'line', None)) + continue + uniquename = self.dlfiles.add_file(docname, filepath) + node['filename'] = uniquename + def process_images(self, docname, doctree): """ Process and rewrite image URIs. """ - existing_names = set(v[1] for v in self.images.itervalues()) docdir = path.dirname(self.doc2path(docname, base=None)) for node in doctree.traverse(nodes.image): # Map the mimetype to the corresponding image. The writer may @@ -650,11 +712,16 @@ class BuildEnvironment: node['candidates'] = candidates = {} imguri = node['uri'] if imguri.find('://') != -1: - self.warn(docname, 'Nonlocal image URI found: %s' % imguri, node.line) + self.warn(docname, 'nonlocal image URI found: %s' % imguri, + node.line) candidates['?'] = imguri continue # imgpath is the image path *from srcdir* - imgpath = path.normpath(path.join(docdir, imguri)) + if imguri.startswith('/') or imguri.startswith(os.sep): + # absolute path (= relative to srcdir) + imgpath = path.normpath(imguri[1:]) + else: + imgpath = path.normpath(path.join(docdir, imguri)) # set imgpath as default URI node['uri'] = imgpath if imgpath.endswith(os.extsep + '*'): @@ -672,7 +739,8 @@ class BuildEnvironment: finally: f.close() except (OSError, IOError): - self.warn(docname, 'Image file %s not readable' % filename) + self.warn(docname, + 'image file %s not readable' % filename) if imgtype: candidates['image/' + imgtype] = new_imgpath else: @@ -682,19 +750,10 @@ class BuildEnvironment: for imgpath in candidates.itervalues(): self.dependencies.setdefault(docname, set()).add(imgpath) if not os.access(path.join(self.srcdir, imgpath), os.R_OK): - self.warn(docname, 'Image file not readable: %s' % imgpath, + self.warn(docname, 'image file not readable: %s' % imgpath, node.line) - if imgpath in self.images: - self.images[imgpath][0].add(docname) continue - uniquename = path.basename(imgpath) - base, ext = path.splitext(uniquename) - i = 0 - while uniquename in existing_names: - i += 1 - uniquename = '%s%s%s' % (base, i, ext) - self.images[imgpath] = (set([docname]), uniquename) - existing_names.add(uniquename) + self.images.add_file(docname, imgpath) def process_metadata(self, docname, doctree): """ @@ -746,7 +805,8 @@ class BuildEnvironment: continue if name in self.labels: self.warn(docname, 'duplicate label %s, ' % name + - 'other instance in %s' % self.doc2path(self.labels[name][0]), + 'other instance in ' + + self.doc2path(self.labels[name][0]), node.line) self.anonlabels[name] = docname, labelid if node.tagname == 'section': @@ -782,6 +842,8 @@ class BuildEnvironment: file relations from it.""" if toctreenode['glob']: self.glob_toctrees.add(docname) + if toctreenode.get('numbered'): + self.numbered_toctrees.add(docname) includefiles = toctreenode['includefiles'] for includefile in includefiles: # note that if the included file is rebuilt, this one must be @@ -861,6 +923,15 @@ class BuildEnvironment: node['refuri'] = node['anchorname'] return toc + def get_toctree_for(self, docname, builder, collapse): + """Return the global TOC nodetree.""" + doctree = self.get_doctree(self.config.master_doc) + for toctreenode in doctree.traverse(addnodes.toctree): + result = self.resolve_toctree(docname, builder, toctreenode, + prune=True, collapse=collapse) + if result is not None: + return result + # ------- # these are called from docutils directives and therefore use self.docname # @@ -868,7 +939,8 @@ class BuildEnvironment: if fullname in self.descrefs: self.warn(self.docname, 'duplicate canonical description name %s, ' % fullname + - 'other instance in %s' % self.doc2path(self.descrefs[fullname][0]), + 'other instance in ' + + self.doc2path(self.descrefs[fullname][0]), line) self.descrefs[fullname] = (self.docname, desctype) @@ -884,12 +956,10 @@ class BuildEnvironment: def note_versionchange(self, type, version, node, lineno): self.versionchanges.setdefault(version, []).append( - (type, self.docname, lineno, self.currmodule, self.currdesc, node.astext())) + (type, self.docname, lineno, self.currmodule, self.currdesc, + node.astext())) def note_dependency(self, filename): - basename = path.dirname(self.doc2path(self.docname, base=None)) - # this will do the right thing when filename is absolute too - filename = path.join(basename, filename) self.dependencies.setdefault(self.docname, set()).add(filename) # ------- @@ -905,7 +975,7 @@ class BuildEnvironment: f.close() doctree.settings.env = self doctree.reporter = Reporter(self.doc2path(docname), 2, 4, - stream=RedirStream(self._warnfunc)) + stream=WarningStream(self._warnfunc)) return doctree @@ -931,7 +1001,7 @@ class BuildEnvironment: return doctree def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, - titles_only=False): + titles_only=False, collapse=False): """ Resolve a *toctree* node into individual bullet lists with titles as items, returning None (if no containing titles are found) or @@ -941,51 +1011,102 @@ class BuildEnvironment: to the value of the *maxdepth* option on the *toctree* node. If *titles_only* is True, only toplevel document titles will be in the resulting tree. + If *collapse* is True, all branches not containing docname will + be collapsed. """ + if toctree.get('hidden', False): + return None - def _walk_depth(node, depth, maxdepth, titleoverrides): + def _walk_depth(node, depth, maxdepth): """Utility: Cut a TOC at a specified depth.""" for subnode in node.children[:]: - if isinstance(subnode, (addnodes.compact_paragraph, nodes.list_item)): + if isinstance(subnode, (addnodes.compact_paragraph, + nodes.list_item)): subnode['classes'].append('toctree-l%d' % (depth-1)) - _walk_depth(subnode, depth, maxdepth, titleoverrides) + _walk_depth(subnode, depth, maxdepth) elif isinstance(subnode, nodes.bullet_list): if maxdepth > 0 and depth > maxdepth: subnode.parent.replace(subnode, []) else: - _walk_depth(subnode, depth+1, maxdepth, titleoverrides) - - def _entries_from_toctree(toctreenode, separate=False): + _walk_depth(subnode, depth+1, maxdepth) + + # cull sub-entries whose parents aren't 'current' + if (collapse and + depth > 1 and + 'current' not in subnode.parent['classes']): + subnode.parent.remove(subnode) + + elif isinstance(subnode, nodes.reference): + # identify the toc entry pointing to the current document + if subnode['refuri'] == docname and \ + not subnode['anchorname']: + # tag the whole branch as 'current' + p = subnode + while p: + p['classes'].append('current') + p = p.parent + + def _entries_from_toctree(toctreenode, separate=False, subtree=False): """Return TOC entries for a toctree node.""" - includefiles = map(str, toctreenode['includefiles']) - + refs = [(e[0], str(e[1])) for e in toctreenode['entries']] entries = [] - for includefile in includefiles: + for (title, ref) in refs: try: - toc = self.tocs[includefile].deepcopy() + if url_re.match(ref): + reference = nodes.reference('', '', + refuri=ref, anchorname='', + *[nodes.Text(title)]) + para = addnodes.compact_paragraph('', '', reference) + item = nodes.list_item('', para) + toc = nodes.bullet_list('', item) + elif ref == 'self': + # 'self' refers to the document from which this + # toctree originates + ref = toctreenode['parent'] + if not title: + title = self.titles[ref].astext() + reference = nodes.reference('', '', + refuri=ref, + anchorname='', + *[nodes.Text(title)]) + para = addnodes.compact_paragraph('', '', reference) + item = nodes.list_item('', para) + # don't show subitems + toc = nodes.bullet_list('', item) + else: + toc = self.tocs[ref].deepcopy() + if title and toc.children and len(toc.children) == 1: + child = toc.children[0] + for refnode in child.traverse(nodes.reference): + if refnode['refuri'] == ref and \ + not refnode['anchorname']: + refnode.children = [nodes.Text(title)] if not toc.children: # empty toc means: no titles will show up in the toctree - self.warn(docname, 'toctree contains reference to document ' - '%r that doesn\'t have a title: no link will be ' - 'generated' % includefile) + self.warn(docname, + 'toctree contains reference to document ' + '%r that doesn\'t have a title: no link ' + 'will be generated' % ref) except KeyError: # this is raised if the included file does not exist - self.warn(docname, 'toctree contains reference to nonexisting ' - 'document %r' % includefile) + self.warn(docname, 'toctree contains reference to ' + 'nonexisting document %r' % ref) else: # if titles_only is given, only keep the main title and # sub-toctrees if titles_only: - # delete everything but the toplevel title(s) and toctrees + # delete everything but the toplevel title(s) + # and toctrees for toplevel in toc: # nodes with length 1 don't have any children anyway if len(toplevel) > 1: - subtoctrees = toplevel.traverse(addnodes.toctree) - toplevel[1][:] = subtoctrees + subtrees = toplevel.traverse(addnodes.toctree) + toplevel[1][:] = subtrees # resolve all sub-toctrees for toctreenode in toc.traverse(addnodes.toctree): i = toctreenode.parent.index(toctreenode) + 1 - for item in _entries_from_toctree(toctreenode): + for item in _entries_from_toctree(toctreenode, + subtree=True): toctreenode.parent.insert(i, item) i += 1 toctreenode.parent.remove(toctreenode) @@ -993,32 +1114,38 @@ class BuildEnvironment: entries.append(toc) else: entries.extend(toc.children) + if not subtree and not separate: + ret = nodes.bullet_list() + ret += entries + return [ret] return entries maxdepth = maxdepth or toctree.get('maxdepth', -1) - titleoverrides = toctree.get('includetitles', {}) - tocentries = _entries_from_toctree(toctree, separate=True) + # NOTE: previously, this was separate=True, but that leads to artificial + # separation when two or more toctree entries form a logical unit, so + # separating mode is no longer used -- it's kept here for history's sake + tocentries = _entries_from_toctree(toctree, separate=False) if not tocentries: return None newnode = addnodes.compact_paragraph('', '', *tocentries) newnode['toctree'] = True + # prune the tree to maxdepth and replace titles, also set level classes - _walk_depth(newnode, 1, prune and maxdepth or 0, titleoverrides) - # replace titles, if needed, and set the target paths in the - # toctrees (they are not known at TOC generation time) + _walk_depth(newnode, 1, prune and maxdepth or 0) + + # set the target paths in the toctrees (they are not known at TOC + # generation time) for refnode in newnode.traverse(nodes.reference): - if titleoverrides and not refnode['anchorname'] \ - and refnode['refuri'] in titleoverrides: - newtitle = titleoverrides[refnode['refuri']] - refnode.children = [nodes.Text(newtitle)] - refnode['refuri'] = builder.get_relative_uri( - docname, refnode['refuri']) + refnode['anchorname'] + if not url_re.match(refnode['refuri']): + refnode['refuri'] = builder.get_relative_uri( + docname, refnode['refuri']) + refnode['anchorname'] return newnode - descroles = frozenset(('data', 'exc', 'func', 'class', 'const', 'attr', 'obj', - 'meth', 'cfunc', 'cmember', 'cdata', 'ctype', 'cmacro')) + descroles = frozenset(('data', 'exc', 'func', 'class', 'const', + 'attr', 'obj', 'meth', 'cfunc', 'cmember', + 'cdata', 'ctype', 'cmacro')) def resolve_references(self, doctree, fromdocname, builder): reftarget_roles = set(('token', 'term', 'citation')) @@ -1035,31 +1162,33 @@ class BuildEnvironment: try: if typ == 'ref': if node['refcaption']: - # reference to anonymous label; the reference uses the supplied - # link caption + # reference to anonymous label; the reference uses + # the supplied link caption docname, labelid = self.anonlabels.get(target, ('','')) sectname = node.astext() if not docname: - self.warn(fromdocname, 'undefined label: %s' % target, - node.line) + self.warn(fromdocname, 'undefined label: %s' % + target, node.line) else: - # reference to the named label; the final node will contain the - # section name after the label - docname, labelid, sectname = self.labels.get(target, ('','','')) + # reference to the named label; the final node will + # contain the section name after the label + docname, labelid, sectname = self.labels.get(target, + ('','','')) if not docname: self.warn( fromdocname, - 'undefined label: %s -- if you don\'t ' % target + - 'give a link caption the label must precede a section ' - 'header.', node.line) + 'undefined label: %s' % target + ' -- if you ' + 'don\'t give a link caption the label must ' + 'precede a section header.', node.line) if docname: newnode = nodes.reference('', '') innernode = nodes.emphasis(sectname, sectname) if docname == fromdocname: newnode['refid'] = labelid else: - # set more info in contnode in case the get_relative_uri call - # raises NoUri, the builder will then have to resolve these + # set more info in contnode; in case the + # get_relative_uri call raises NoUri, + # the builder will then have to resolve these contnode = addnodes.pending_xref('') contnode['refdocname'] = docname contnode['refsectname'] = sectname @@ -1070,6 +1199,25 @@ class BuildEnvironment: newnode.append(innernode) else: newnode = contnode + elif typ == 'doc': + # directly reference to document by source name; + # can be absolute or relative + docname = docname_join(fromdocname, target) + if docname not in self.all_docs: + self.warn(fromdocname, 'unknown document: %s' % docname, + node.line) + newnode = contnode + else: + if node['refcaption']: + # reference with explicit title + caption = node.astext() + else: + caption = self.titles[docname].astext() + innernode = nodes.emphasis(caption, caption) + newnode = nodes.reference('', '') + newnode['refuri'] = builder.get_relative_uri( + fromdocname, docname) + newnode.append(innernode) elif typ == 'keyword': # keywords are referenced by named labels docname, labelid, _ = self.labels.get(target, ('','','')) @@ -1086,7 +1234,8 @@ class BuildEnvironment: newnode.append(contnode) elif typ == 'option': progname = node['refprogram'] - docname, labelid = self.progoptions.get((progname, target), ('', '')) + docname, labelid = self.progoptions.get((progname, target), + ('', '')) if not docname: newnode = contnode else: @@ -1098,13 +1247,16 @@ class BuildEnvironment: fromdocname, docname) + '#' + labelid newnode.append(contnode) elif typ in reftarget_roles: - docname, labelid = self.reftargets.get((typ, target), ('', '')) + docname, labelid = self.reftargets.get((typ, target), + ('', '')) if not docname: if typ == 'term': - self.warn(fromdocname, 'term not in glossary: %s' % target, + self.warn(fromdocname, + 'term not in glossary: %s' % target, node.line) elif typ == 'citation': - self.warn(fromdocname, 'citation not found: %s' % target, + self.warn(fromdocname, + 'citation not found: %s' % target, node.line) newnode = contnode else: @@ -1119,8 +1271,8 @@ class BuildEnvironment: docname, synopsis, platform, deprecated = \ self.modules.get(target, ('','','', '')) if not docname: - newnode = builder.app.emit_firstresult('missing-reference', - self, node, contnode) + newnode = builder.app.emit_firstresult( + 'missing-reference', self, node, contnode) if not newnode: newnode = contnode elif docname == fromdocname: @@ -1142,8 +1294,8 @@ class BuildEnvironment: name, desc = self.find_desc(modname, clsname, target, typ, searchorder) if not desc: - newnode = builder.app.emit_firstresult('missing-reference', - self, node, contnode) + newnode = builder.app.emit_firstresult( + 'missing-reference', self, node, contnode) if not newnode: newnode = contnode else: @@ -1157,15 +1309,80 @@ class BuildEnvironment: newnode['reftitle'] = name newnode.append(contnode) else: - raise RuntimeError('unknown xfileref node encountered: %s' % node) + raise RuntimeError('unknown xfileref node encountered: %s' + % node) except NoUri: newnode = contnode if newnode: node.replace_self(newnode) + for node in doctree.traverse(addnodes.only): + try: + ret = builder.tags.eval_condition(node['expr']) + except Exception, err: + self.warn(fromdocname, 'exception while evaluating only ' + 'directive expression: %s' % err, node.line) + node.replace_self(node.children) + else: + if ret: + node.replace_self(node.children) + else: + node.replace_self([]) + # allow custom references to be resolved builder.app.emit('doctree-resolved', doctree, fromdocname) + def assign_section_numbers(self): + """Assign a section number to each heading under a numbered toctree.""" + # a list of all docnames whose section numbers changed + rewrite_needed = [] + + old_secnumbers = self.toc_secnumbers + self.toc_secnumbers = {} + + def _walk_toc(node, secnums, titlenode=None): + # titlenode is the title of the document, it will get assigned a + # secnumber too, so that it shows up in next/prev/parent rellinks + for subnode in node.children: + if isinstance(subnode, nodes.bullet_list): + numstack.append(0) + _walk_toc(subnode, secnums, titlenode) + numstack.pop() + titlenode = None + elif isinstance(subnode, nodes.list_item): + _walk_toc(subnode, secnums, titlenode) + titlenode = None + elif isinstance(subnode, addnodes.compact_paragraph): + numstack[-1] += 1 + secnums[subnode[0]['anchorname']] = \ + subnode[0]['secnumber'] = tuple(numstack) + if titlenode: + titlenode['secnumber'] = tuple(numstack) + titlenode = None + elif isinstance(subnode, addnodes.toctree): + _walk_toctree(subnode) + + def _walk_toctree(toctreenode): + for (title, ref) in toctreenode['entries']: + if url_re.match(ref) or ref == 'self': + # don't mess with those + continue + if ref in self.tocs: + secnums = self.toc_secnumbers[ref] = {} + _walk_toc(self.tocs[ref], secnums, self.titles.get(ref)) + if secnums != old_secnumbers.get(ref): + rewrite_needed.append(ref) + + for docname in self.numbered_toctrees: + doctree = self.get_doctree(docname) + for toctreenode in doctree.traverse(addnodes.toctree): + if toctreenode.get('numbered'): + # every numbered toctree gets new numbering + numstack = [0] + _walk_toctree(toctreenode) + + return rewrite_needed + def create_index(self, builder, _fixre=re.compile(r'(.*) ([(][^()]*[)])')): """Create the real index from the collected index entries.""" new = {} @@ -1241,8 +1458,10 @@ class BuildEnvironment: m = _fixre.match(key) if m: if oldkey == m.group(1): - # prefixes match: add entry as subitem of the previous entry - oldsubitems.setdefault(m.group(2), [[], {}])[0].extend(targets) + # prefixes match: add entry as subitem of the + # previous entry + oldsubitems.setdefault(m.group(2), [[], {}])[0].\ + extend(targets) del newlist[i] continue oldkey = m.group(1) @@ -1262,7 +1481,8 @@ class BuildEnvironment: else: # get all other symbols under one heading return 'Symbols' - return [(key, list(group)) for (key, group) in groupby(newlist, keyfunc)] + return [(key, list(group)) + for (key, group) in groupby(newlist, keyfunc)] def collect_relations(self): relations = {} @@ -1301,7 +1521,8 @@ class BuildEnvironment: # else it will stay None # same for children if includes: - for subindex, args in enumerate(izip(includes, [None] + includes, + for subindex, args in enumerate(izip(includes, + [None] + includes, includes[1:] + [None])): collect([(docname, subindex)] + parents, *args) relations[docname] = [parents[0][0], previous, next] @@ -1369,14 +1590,16 @@ class BuildEnvironment: def find_keyword(self, keyword, avoid_fuzzy=False, cutoff=0.6, n=20): """ - Find keyword matches for a keyword. If there's an exact match, just return - it, else return a list of fuzzy matches if avoid_fuzzy isn't True. + Find keyword matches for a keyword. If there's an exact match, + just return it, else return a list of fuzzy matches if avoid_fuzzy + isn't True. Keywords searched are: first modules, then descrefs. Returns: None if nothing found (type, docname, anchorname) if exact match found - list of (quality, type, docname, anchorname, description) if fuzzy + list of (quality, type, docname, anchorname, description) + if fuzzy """ if keyword in self.modules: diff --git a/sphinx/errors.py b/sphinx/errors.py new file mode 100644 index 00000000..d9b8b6b8 --- /dev/null +++ b/sphinx/errors.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +""" + sphinx.errors + ~~~~~~~~~~~~~ + + Contains SphinxError and a few subclasses (in an extra module to avoid + circular import problems). + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +class SphinxError(Exception): + """ + Base class for Sphinx errors that are shown to the user in a nicer + way than normal exceptions. + """ + category = 'Sphinx error' + + +class SphinxWarning(SphinxError): + """Raised for warnings if warnings are treated as errors.""" + category = 'Warning, treated as error' + + +class ExtensionError(SphinxError): + """Raised if something's wrong with the configuration.""" + category = 'Extension error' + + def __init__(self, message, orig_exc=None): + super(ExtensionError, self).__init__(message) + self.orig_exc = orig_exc + + def __repr__(self): + if self.orig_exc: + return '%s(%r, %r)' % (self.__class__.__name__, + self.message, self.orig_exc) + return '%s(%r)' % (self.__class__.__name__, self.message) + + def __str__(self): + parent_str = super(ExtensionError, self).__str__() + if self.orig_exc: + return '%s (exception: %s)' % (parent_str, self.orig_exc) + return parent_str + + +class ThemeError(SphinxError): + category = 'Theme error' diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index e8df7c1c..72d50029 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -13,40 +13,82 @@ import re import sys -import types import inspect -import linecache -from types import FunctionType, BuiltinMethodType, MethodType +from types import ModuleType, FunctionType, BuiltinFunctionType, MethodType, \ + ClassType from docutils import nodes -from docutils.parsers.rst import directives +from docutils.utils import assemble_option_dict from docutils.statemachine import ViewList -from sphinx.util import rpartition, nested_parse_with_titles -from sphinx.directives.desc import py_sig_re +from sphinx.util import rpartition, nested_parse_with_titles, force_decode +from sphinx.pycode import ModuleAnalyzer, PycodeError +from sphinx.application import ExtensionError +from sphinx.util.compat import Directive +from sphinx.util.docstrings import prepare_docstring + try: base_exception = BaseException except NameError: base_exception = Exception -_charset_re = re.compile(r'coding[:=]\s*([-\w.]+)') -_module_charsets = {} - -class Options(object): - pass +#: extended signature RE: with explicit module name separated by :: +py_ext_sig_re = re.compile( + r'''^ ([\w.]+::)? # explicit module name + ([\w.]+\.)? # module and/or class name(s) + (\w+) \s* # thing name + (?: \((.*)\) # optional: arguments + (?:\s* -> \s* (.*))? # return annotation + )? $ # and nothing more + ''', re.VERBOSE) -def is_static_method(obj): - """Check if the object given is a static method.""" - if isinstance(obj, (FunctionType, classmethod)): +class DefDict(dict): + """A dict that returns a default on nonexisting keys.""" + def __init__(self, default): + dict.__init__(self) + self.default = default + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + return self.default + def __nonzero__(self): + # docutils check "if option_spec" return True - elif isinstance(obj, BuiltinMethodType): - return obj.__self__ is not None - elif isinstance(obj, MethodType): - return obj.im_self is not None - return False + +identity = lambda x: x + + +class Options(dict): + """A dict/attribute hybrid that returns None on nonexisting keys.""" + def __getattr__(self, name): + try: + return self[name.replace('_', '-')] + except KeyError: + return None + + +ALL = object() + +def members_option(arg): + """Used to convert the :members: option to auto directives.""" + if arg is None: + return ALL + return [x.strip() for x in arg.split(',')] + +def members_set_option(arg): + """Used to convert the :members: option to auto directives.""" + if arg is None: + return ALL + return set(x.strip() for x in arg.split(',')) + +def bool_option(arg): + """Used to convert flag options to auto directives. (Instead of + directives.flag(), which returns None.)""" + return True class AutodocReporter(object): @@ -155,501 +197,924 @@ def between(marker, what=None, keepempty=False): def isdescriptor(x): """Check if the object is some kind of descriptor.""" for item in '__get__', '__set__', '__delete__': - if callable(getattr(x, item, None)): + if hasattr(getattr(x, item, None), '__call__'): return True return False -def prepare_docstring(s): +class Documenter(object): """ - Convert a docstring into lines of parseable reST. Return it as a list of - lines usable for inserting into a docutils ViewList (used as argument - of nested_parse().) An empty line is added to act as a separator between - this docstring and following content. + A Documenter knows how to autodocument a single object type. When + registered with the AutoDirective, it will be used to document objects + of that type when needed by autodoc. + + Its *objtype* attribute selects what auto directive it is assigned to + (the directive name is 'auto' + objtype), and what directive it generates + by default, though that can be overridden by an attribute called + *directivetype*. + + A Documenter has an *option_spec* that works like a docutils directive's; + in fact, it will be used to parse an auto directive's options that matches + the documenter. """ - lines = s.expandtabs().splitlines() - # Find minimum indentation of any non-blank lines after first line. - margin = sys.maxint - for line in lines[1:]: - content = len(line.lstrip()) - if content: - indent = len(line) - content - margin = min(margin, indent) - # Remove indentation. - if lines: - lines[0] = lines[0].lstrip() - if margin < sys.maxint: - for i in range(1, len(lines)): lines[i] = lines[i][margin:] - # Remove any leading blank lines. - while lines and not lines[0]: - lines.pop(0) - # make sure there is an empty line at the end - if lines and lines[-1]: - lines.append('') - return lines - - -def get_module_charset(module): - """Return the charset of the given module (cached in _module_charsets).""" - if module in _module_charsets: - return _module_charsets[module] - try: - filename = __import__(module, None, None, ['foo']).__file__ - except (ImportError, AttributeError): - return None - if filename[-4:].lower() in ('.pyc', '.pyo'): - filename = filename[:-1] - for line in [linecache.getline(filename, x) for x in (1, 2)]: - match = _charset_re.search(line) - if match is not None: - charset = match.group(1) - break - else: - charset = 'ascii' - _module_charsets[module] = charset - return charset - - -class RstGenerator(object): - def __init__(self, options, document, lineno): - self.options = options - self.env = document.settings.env - self.reporter = document.reporter - self.lineno = lineno - self.filename_set = set() - self.warnings = [] - self.result = ViewList() - - def warn(self, msg): - self.warnings.append(self.reporter.warning(msg, line=self.lineno)) + #: name by which the directive is called (auto...) and the default + #: generated directive name + objtype = 'object' + #: indentation by which to indent the directive content + content_indent = u' ' + #: priority if multiple documenters return True from can_document_member + priority = 0 + #: order if autodoc_member_order is set to 'groupwise' + member_order = 0 + + option_spec = {'noindex': bool_option} + + @staticmethod + def get_attr(obj, name, *defargs): + """getattr() override for types such as Zope interfaces.""" + for typ, func in AutoDirective._special_attrgetters.iteritems(): + if isinstance(obj, typ): + return func(obj, name, *defargs) + return getattr(obj, name, *defargs) + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + """Called to see if a member can be documented by this documenter.""" + raise NotImplementedError('must be implemented in subclasses') + + def __init__(self, directive, name, indent=u''): + self.directive = directive + self.env = directive.env + self.options = directive.genopt + self.name = name + self.indent = indent + # the module and object path within the module, and the fully + # qualified name (all set after resolve_name succeeds) + self.modname = None + self.module = None + self.objpath = None + self.fullname = None + # extra signature items (arguments and return annotation, + # also set after resolve_name succeeds) + self.args = None + self.retann = None + # the object to document (set after import_object succeeds) + self.object = None + # the module analyzer to get at attribute docs, or None + self.analyzer = None + + def add_line(self, line, source, *lineno): + """Append one line of generated reST to the output.""" + self.directive.result.append(self.indent + line, source, *lineno) + + def resolve_name(self, modname, parents, path, base): + """ + Resolve the module and name of the object to document given by the + arguments and the current module/class. - def get_doc(self, what, name, obj): - """Format and yield lines of the docstring(s) for the object.""" - docstrings = [] - if getattr(obj, '__doc__', None): - docstrings.append(obj.__doc__) - # skip some lines in module docstrings if configured - if what == 'module' and self.env.config.automodule_skip_lines and docstrings: - docstrings[0] = '\n'.join(docstrings[0].splitlines() - [self.env.config.automodule_skip_lines:]) - # for classes, what the "docstring" is can be controlled via an option - if what in ('class', 'exception'): - content = self.env.config.autoclass_content - if content in ('both', 'init'): - initdocstring = getattr(obj, '__init__', None).__doc__ - # for new-style classes, no __init__ means default __init__ - if initdocstring == object.__init__.__doc__: - initdocstring = None - if initdocstring: - if content == 'init': - docstrings = [initdocstring] - else: - docstrings.append(initdocstring) - # the default is only the class docstring - - # decode the docstrings using the module's source encoding - charset = None - module = getattr(obj, '__module__', None) - if module is not None: - charset = get_module_charset(module) - - for docstring in docstrings: - if isinstance(docstring, str): - if charset: - docstring = docstring.decode(charset) - else: - try: - # try decoding with utf-8, should only work for real UTF-8 - docstring = docstring.decode('utf-8') - except UnicodeError: - # last resort -- can't fail - docstring = docstring.decode('latin1') - docstringlines = prepare_docstring(docstring) - if self.env.app: - # let extensions preprocess docstrings - self.env.app.emit('autodoc-process-docstring', - what, name, obj, self.options, docstringlines) - for line in docstringlines: - yield line + Must return a pair of the module name and a chain of attributes; for + example, it would return ``('zipfile', ['ZipFile', 'open'])`` for the + ``zipfile.ZipFile.open`` method. + """ + raise NotImplementedError('must be implemented in subclasses') - def resolve_name(self, what, name): + def parse_name(self): """ Determine what module to import and what attribute to document. - Returns a tuple of: the full name, the module name, a path of - names to get via getattr, the signature and return annotation. + Returns True and sets *self.modname*, *self.objpath*, *self.fullname*, + *self.args* and *self.retann* if parsing and resolving was successful. """ - # first, parse the definition -- auto directives for classes and functions - # can contain a signature which is then used instead of an autogenerated one + # first, parse the definition -- auto directives for classes and + # functions can contain a signature which is then used instead of + # an autogenerated one try: - path, base, args, retann = py_sig_re.match(name).groups() - except: - self.warn('invalid signature for auto%s (%r)' % (what, name)) - return name, name, [], None, None - # fullname is the fully qualified name, base the name after the last dot - fullname = (path or '') + base - - if what == 'module': - if args or retann: - self.warn('ignoring signature arguments and return annotation ' - 'for automodule %s' % fullname) - return fullname, fullname, [], None, None - - elif what in ('class', 'exception', 'function'): - if path: - mod = path.rstrip('.') - else: - mod = None - # if documenting a toplevel object without explicit module, it can - # be contained in another auto directive ... - if hasattr(self.env, 'autodoc_current_module'): - mod = self.env.autodoc_current_module - # ... or in the scope of a module directive - if not mod: - mod = self.env.currmodule - return fullname, mod, [base], args, retann - + explicit_modname, path, base, args, retann = \ + py_ext_sig_re.match(self.name).groups() + except AttributeError: + self.directive.warn('invalid signature for auto%s (%r)' % + (self.objtype, self.name)) + return False + + # support explicit module and class name separation via :: + if explicit_modname is not None: + modname = explicit_modname[:-2] + parents = path and path.rstrip('.').split('.') or [] else: - if path: - mod_cls = path.rstrip('.') - else: - mod_cls = None - # if documenting a class-level object without path, there must be a - # current class, either from a parent auto directive ... - if hasattr(self.env, 'autodoc_current_class'): - mod_cls = self.env.autodoc_current_class - # ... or from a class directive - if mod_cls is None: - mod_cls = self.env.currclass - # ... if still None, there's no way to know - if mod_cls is None: - return fullname, None, [], args, retann - mod, cls = rpartition(mod_cls, '.') - # if the module name is still missing, get it like above - if not mod and hasattr(self.env, 'autodoc_current_module'): - mod = self.env.autodoc_current_module - if not mod: - mod = self.env.currmodule - return fullname, mod, [cls, base], args, retann + modname = None + parents = [] + + self.modname, self.objpath = \ + self.resolve_name(modname, parents, path, base) + + if not self.modname: + return False + + self.args = args + self.retann = retann + self.fullname = (self.modname or '') + \ + (self.objpath and '.' + '.'.join(self.objpath) or '') + return True - def format_signature(self, what, name, obj, args, retann): + def import_object(self): """ - Return the signature of the object, formatted for display. + Import the object given by *self.modname* and *self.objpath* and sets + it as *self.object*. + + Returns True if successful, False if an error occurred. """ - if what not in ('class', 'method', 'function'): - return '' + try: + __import__(self.modname) + obj = self.module = sys.modules[self.modname] + for part in self.objpath: + obj = self.get_attr(obj, part) + self.object = obj + return True + except (ImportError, AttributeError), err: + self.directive.warn( + 'autodoc can\'t import/find %s %r, it reported error: ' + '"%s", please check your spelling and sys.path' % + (self.objtype, str(self.fullname), err)) + return False - err = None - if args is not None: + def get_real_modname(self): + """ + Get the real module name of an object to document. (It can differ + from the name of the module through which the object was imported.) + """ + return self.get_attr(self.object, '__module__', None) or self.modname + + def check_module(self): + """ + Check if *self.object* is really defined in the module given by + *self.modname*. + """ + modname = self.get_attr(self.object, '__module__', None) + if modname and modname != self.modname: + return False + return True + + def format_args(self): + """ + Format the argument signature of *self.object*. Should return None if + the object does not have a signature. + """ + return None + + def format_signature(self): + """ + Format the signature (arguments and return annotation) of the object. + Let the user process it via the ``autodoc-process-signature`` event. + """ + if self.args is not None: # signature given explicitly - args = "(%s)" % args + args = "(%s)" % self.args else: # try to introspect the signature - try: - args = None - getargs = True - if what == 'class': - # for classes, the relevant signature is the __init__ method's - obj = getattr(obj, '__init__', None) - # classes without __init__ method, default __init__ or - # __init__ written in C? - if obj is None or obj is object.__init__ or not \ - (inspect.ismethod(obj) or inspect.isfunction(obj)): - getargs = False - elif inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj): - # can never get arguments of a C function or method - getargs = False - if getargs: - argspec = inspect.getargspec(obj) - if what in ('class', 'method') and argspec[0] and \ - argspec[0][0] in ('cls', 'self'): - del argspec[0][0] - args = inspect.formatargspec(*argspec) - except Exception, e: - args = None - err = e - - result = self.env.app.emit_firstresult('autodoc-process-signature', what, - name, obj, self.options, args, retann) + args = self.format_args() + if args is None: + return '' + retann = self.retann + + result = self.env.app.emit_firstresult( + 'autodoc-process-signature', self.objtype, self.fullname, + self.object, self.options, args, retann) if result: args, retann = result if args is not None: - return '%s%s' % (args, retann or '') - elif err: - # re-raise the error for perusal of the handler in generate() - raise RuntimeError(err) + return args + (retann and (' -> %s' % retann) or '') else: return '' - def generate(self, what, name, members, add_content, indent=u'', check_module=False): + def add_directive_header(self, sig): + """Add the directive header and options to the generated content.""" + directive = getattr(self, 'directivetype', self.objtype) + # the name to put into the generated directive -- doesn't contain + # the module (except for module directive of course) + name_in_directive = '.'.join(self.objpath) or self.modname + self.add_line(u'.. %s:: %s%s' % (directive, name_in_directive, sig), + '<autodoc>') + if self.options.noindex: + self.add_line(u' :noindex:', '<autodoc>') + if self.objpath: + # Be explicit about the module, this is necessary since .. class:: + # etc. don't support a prepended module name + self.add_line(u' :module: %s' % self.modname, '<autodoc>') + + def get_doc(self, encoding=None): + """Decode and return lines of the docstring(s) for the object.""" + docstring = self.get_attr(self.object, '__doc__', None) + if docstring: + # make sure we have Unicode docstrings, then sanitize and split + # into lines + return [prepare_docstring(force_decode(docstring, encoding))] + return [] + + def process_doc(self, docstrings): + """Let the user process the docstrings before adding them.""" + for docstringlines in docstrings: + if self.env.app: + # let extensions preprocess docstrings + self.env.app.emit('autodoc-process-docstring', + self.objtype, self.fullname, self.object, + self.options, docstringlines) + for line in docstringlines: + yield line + + def add_content(self, more_content, no_docstring=False): + """Add content from docstrings, attribute documentation and user.""" + # set sourcename and add content from attribute documentation + if self.analyzer: + # prevent encoding errors when the file name is non-ASCII + filename = unicode(self.analyzer.srcname, + sys.getfilesystemencoding(), 'replace') + sourcename = u'%s:docstring of %s' % (filename, self.fullname) + + attr_docs = self.analyzer.find_attr_docs() + if self.objpath: + key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) + if key in attr_docs: + no_docstring = True + docstrings = [attr_docs[key]] + for i, line in enumerate(self.process_doc(docstrings)): + self.add_line(line, sourcename, i) + else: + sourcename = u'docstring of %s' % self.fullname + + # add content from docstrings + if not no_docstring: + encoding = self.analyzer and self.analyzer.encoding + docstrings = self.get_doc(encoding) + for i, line in enumerate(self.process_doc(docstrings)): + self.add_line(line, sourcename, i) + + # add additional content (e.g. from document), if present + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) + + def get_object_members(self, want_all): + """ + Return `(members_check_module, members)` where `members` is a + list of `(membername, member)` pairs of the members of *self.object*. + + If *want_all* is True, return all members. Else, only return those + members given by *self.options.members* (which may also be none). + """ + if not want_all: + if not self.options.members: + return False, [] + # specific members given + ret = [] + for mname in self.options.members: + try: + ret.append((mname, self.get_attr(self.object, mname))) + except AttributeError: + self.directive.warn('missing attribute %s in object %s' + % (mname, self.fullname)) + return False, ret + elif self.options.inherited_members: + # getmembers() uses dir() which pulls in members from all + # base classes + return False, inspect.getmembers(self.object) + else: + # __dict__ contains only the members directly defined in + # the class (but get them via getattr anyway, to e.g. get + # unbound method objects instead of function objects) + return False, sorted([ + (mname, self.get_attr(self.object, mname)) + for mname in self.get_attr(self.object, '__dict__')]) + + def filter_members(self, members, want_all): + """ + Filter the given member list: members are skipped if + + - they are private (except if given explicitly) + - they are undocumented (except if undoc-members is given) + + The user can override the skipping decision by connecting to the + ``autodoc-skip-member`` event. + """ + ret = [] + + # search for members in source code too + namespace = '.'.join(self.objpath) # will be empty for modules + + if self.analyzer: + attr_docs = self.analyzer.find_attr_docs() + else: + attr_docs = {} + + # process members and determine which to skip + for (membername, member) in members: + # if isattr is True, the member is documented as an attribute + isattr = False + + if want_all and membername.startswith('_'): + # ignore members whose name starts with _ by default + skip = True + elif (namespace, membername) in attr_docs: + # keep documented attributes + skip = False + isattr = True + else: + # ignore undocumented members if :undoc-members: + # is not given + doc = self.get_attr(member, '__doc__', None) + skip = not self.options.undoc_members and not doc + + # give the user a chance to decide whether this member + # should be skipped + if self.env.app: + # let extensions preprocess docstrings + skip_user = self.env.app.emit_firstresult( + 'autodoc-skip-member', self.objtype, membername, member, + skip, self.options) + if skip_user is not None: + skip = skip_user + if skip: + continue + + ret.append((membername, member, isattr)) + + return ret + + def document_members(self, all_members=False): """ - Generate reST for the object in self.result. + Generate reST for member documentation. If *all_members* is True, + do all members, else those given by *self.options.members*. """ - fullname, mod, objpath, args, retann = self.resolve_name(what, name) - if not mod: + # set current namespace for finding members + self.env.autodoc_current_module = self.modname + if self.objpath: + self.env.autodoc_current_class = self.objpath[0] + + want_all = all_members or self.options.inherited_members or \ + self.options.members is ALL + # find out which members are documentable + members_check_module, members = self.get_object_members(want_all) + + # remove members given by exclude-members + if self.options.exclude_members: + members = [(membername, member) for (membername, member) in members + if membername not in self.options.exclude_members] + + # document non-skipped members + memberdocumenters = [] + for (mname, member, isattr) in self.filter_members(members, want_all): + classes = [cls for cls in AutoDirective._registry.itervalues() + if cls.can_document_member(member, mname, isattr, self)] + if not classes: + # don't know how to document this member + continue + # prefer the documenter with the highest priority + classes.sort(key=lambda cls: cls.priority) + # give explicitly separated module name, so that members + # of inner classes can be documented + full_mname = self.modname + '::' + \ + '.'.join(self.objpath + [mname]) + memberdocumenters.append( + classes[-1](self.directive, full_mname, self.indent)) + + if (self.options.member_order or self.env.config.autodoc_member_order) \ + == 'groupwise': + # sort by group; relies on stable sort to keep items in the + # same group sorted alphabetically + memberdocumenters.sort(key=lambda d: d.member_order) + + for documenter in memberdocumenters: + documenter.generate(all_members=True, + real_modname=self.real_modname, + check_module=members_check_module) + + # reset current objects + self.env.autodoc_current_module = None + self.env.autodoc_current_class = None + + def generate(self, more_content=None, real_modname=None, + check_module=False, all_members=False): + """ + Generate reST for the object given by *self.name*, and possibly members. + + If *more_content* is given, include that content. If *real_modname* is + given, use that module name to find attribute docs. If *check_module* is + True, only generate if the object is defined in the module name it is + imported from. If *all_members* is True, document all members. + """ + if not self.parse_name(): # need a module to import - self.warn('don\'t know which module to import for autodocumenting %r ' - '(try placing a "module" or "currentmodule" directive in the ' - 'document, or giving an explicit module name)' % fullname) + self.directive.warn( + 'don\'t know which module to import for autodocumenting ' + '%r (try placing a "module" or "currentmodule" directive ' + 'in the document, or giving an explicit module name)' + % self.name) return - # the name to put into the generated directive -- doesn't contain the module - name_in_directive = '.'.join(objpath) or mod - # now, import the module and get object to document - try: - todoc = module = __import__(mod, None, None, ['foo']) - if hasattr(module, '__file__') and module.__file__: - modfile = module.__file__ - if modfile[-4:].lower() in ('.pyc', '.pyo'): - modfile = modfile[:-1] - self.filename_set.add(modfile) - else: - modfile = None # e.g. for builtin and C modules - for part in objpath: - todoc = getattr(todoc, part) - except (ImportError, AttributeError), err: - self.warn('autodoc can\'t import/find %s %r, it reported error: "%s", ' - 'please check your spelling and sys.path' % - (what, str(fullname), err)) + if not self.import_object(): return - # check __module__ of object if wanted (for members not given explicitly) + # If there is no real module defined, figure out which to use. + # The real module is used in the module analyzer to look up the module + # where the attribute documentation would actually be found in. + # This is used for situations where you have a module that collects the + # functions and classes of internal submodules. + self.real_modname = real_modname or self.get_real_modname() + + # try to also get a source code analyzer for attribute docs + try: + self.analyzer = ModuleAnalyzer.for_module(self.real_modname) + # parse right now, to get PycodeErrors on parsing + self.analyzer.parse() + except PycodeError, err: + # no source file -- e.g. for builtin and C modules + self.analyzer = None + else: + self.directive.filename_set.add(self.analyzer.srcname) + + # check __module__ of object (for members not given explicitly) if check_module: - if hasattr(todoc, '__module__'): - if todoc.__module__ != mod: - return + if not self.check_module(): + return + + # make sure that the result starts with an empty line. This is + # necessary for some situations where another directive preprocesses + # reST and no starting newline is present + self.add_line(u'', '') # format the object's signature, if any try: - sig = self.format_signature(what, name, todoc, args, retann) + sig = self.format_signature() except Exception, err: - self.warn('error while formatting signature for %s: %s' % - (fullname, err)) + self.directive.warn('error while formatting signature for ' + '%s: %s' % (self.fullname, err)) sig = '' - # make sure that the result starts with an empty line. This is - # necessary for some situations where another directive preprocesses - # reST and no starting newline is present - self.result.append(u'', '') - - # now, create the directive header - directive = (what == 'method' and is_static_method(todoc)) \ - and 'staticmethod' or what - self.result.append(indent + u'.. %s:: %s%s' % - (directive, name_in_directive, sig), '<autodoc>') - if what == 'module': - # Add some module-specific options - if self.options.synopsis: - self.result.append(indent + u' :synopsis: ' + self.options.synopsis, - '<autodoc>') - if self.options.platform: - self.result.append(indent + u' :platform: ' + self.options.platform, - '<autodoc>') - if self.options.deprecated: - self.result.append(indent + u' :deprecated:', '<autodoc>') + # generate the directive header and options, if applicable + self.add_directive_header(sig) + self.add_line(u'', '<autodoc>') + + # e.g. the module directive doesn't have content + self.indent += self.content_indent + + # add all content (from docstrings, attribute docs etc.) + self.add_content(more_content) + + # document members, if possible + self.document_members(all_members) + + +class ModuleDocumenter(Documenter): + """ + Specialized Documenter subclass for modules. + """ + objtype = 'module' + content_indent = u'' + + option_spec = { + 'members': members_option, 'undoc-members': bool_option, + 'noindex': bool_option, 'inherited-members': bool_option, + 'show-inheritance': bool_option, 'synopsis': identity, + 'platform': identity, 'deprecated': bool_option, + 'member-order': identity, 'exclude-members': members_set_option, + } + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + # don't document submodules automatically + return False + + def resolve_name(self, modname, parents, path, base): + if modname is not None: + self.directive.warn('"::" in automodule name doesn\'t make sense') + return (path or '') + base, [] + + def parse_name(self): + ret = Documenter.parse_name(self) + if self.args or self.retann: + self.directive.warn('signature arguments or return annotation ' + 'given for automodule %s' % self.fullname) + return ret + + def add_directive_header(self, sig): + Documenter.add_directive_header(self, sig) + + # add some module-specific options + if self.options.synopsis: + self.add_line( + u' :synopsis: ' + self.options.synopsis, '<autodoc>') + if self.options.platform: + self.add_line( + u' :platform: ' + self.options.platform, '<autodoc>') + if self.options.deprecated: + self.add_line(u' :deprecated:', '<autodoc>') + + def get_object_members(self, want_all): + if want_all: + if not hasattr(self.object, '__all__'): + # for implicit module members, check __module__ to avoid + # documenting imported objects + return True, inspect.getmembers(self.object) + else: + memberlist = self.object.__all__ else: - # Be explicit about the module, this is necessary since .. class:: doesn't - # support a prepended module name - self.result.append(indent + u' :module: %s' % mod, '<autodoc>') - if self.options.noindex: - self.result.append(indent + u' :noindex:', '<autodoc>') - self.result.append(u'', '<autodoc>') + memberlist = self.options.members or [] + ret = [] + for mname in memberlist: + try: + ret.append((mname, getattr(self.object, mname))) + except AttributeError: + self.directive.warn('missing attribute mentioned in :members: ' + 'or __all__: module %s, attribute %s' % + (self.object.__name__, mname)) + return False, ret + + +class ModuleLevelDocumenter(Documenter): + """ + Specialized Documenter subclass for objects on module level (functions, + classes, data/constants). + """ + def resolve_name(self, modname, parents, path, base): + if modname is None: + if path: + modname = path.rstrip('.') + else: + # if documenting a toplevel object without explicit module, + # it can be contained in another auto directive ... + if hasattr(self.env, 'autodoc_current_module'): + modname = self.env.autodoc_current_module + # ... or in the scope of a module directive + if not modname: + modname = self.env.currmodule + # ... else, it stays None, which means invalid + return modname, parents + [base] + + +class ClassLevelDocumenter(Documenter): + """ + Specialized Documenter subclass for objects on class level (methods, + attributes). + """ + def resolve_name(self, modname, parents, path, base): + if modname is None: + if path: + mod_cls = path.rstrip('.') + else: + mod_cls = None + # if documenting a class-level object without path, + # there must be a current class, either from a parent + # auto directive ... + if hasattr(self.env, 'autodoc_current_class'): + mod_cls = self.env.autodoc_current_class + # ... or from a class directive + if mod_cls is None: + mod_cls = self.env.currclass + # ... if still None, there's no way to know + if mod_cls is None: + return None, [] + modname, cls = rpartition(mod_cls, '.') + parents = [cls] + # if the module name is still missing, get it like above + if not modname and hasattr(self.env, 'autodoc_current_module'): + modname = self.env.autodoc_current_module + if not modname: + modname = self.env.currmodule + # ... else, it stays None, which means invalid + return modname, parents + [base] + + +class FunctionDocumenter(ModuleLevelDocumenter): + """ + Specialized Documenter subclass for functions. + """ + objtype = 'function' + member_order = 30 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return isinstance(member, (FunctionType, BuiltinFunctionType)) + + def format_args(self): + if inspect.isbuiltin(self.object) or \ + inspect.ismethoddescriptor(self.object): + # can never get arguments of a C function or method + return None + try: + argspec = inspect.getargspec(self.object) + except TypeError: + # if a class should be documented as function (yay duck + # typing) we try to use the constructor signature as function + # signature without the first argument. + try: + argspec = inspect.getargspec(self.object.__new__) + except TypeError: + argspec = inspect.getargspec(self.object.__init__) + if argspec[0]: + del argspec[0][0] + return inspect.formatargspec(*argspec) + + def document_members(self, all_members=False): + pass + + +class ClassDocumenter(ModuleLevelDocumenter): + """ + Specialized Documenter subclass for classes. + """ + objtype = 'class' + member_order = 20 + option_spec = { + 'members': members_option, 'undoc-members': bool_option, + 'noindex': bool_option, 'inherited-members': bool_option, + 'show-inheritance': bool_option, 'member-order': identity, + 'exclude-members': members_set_option, + } + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return isinstance(member, (type, ClassType)) + + def import_object(self): + ret = ModuleLevelDocumenter.import_object(self) + # if the class is documented under another name, document it + # as data/attribute + if ret: + self.doc_as_attr = (self.objpath[-1] != self.object.__name__) + return ret + + def format_args(self): + args = None + # for classes, the relevant signature is the __init__ method's + initmeth = self.get_attr(self.object, '__init__', None) + # classes without __init__ method, default __init__ or + # __init__ written in C? + if initmeth is None or initmeth is object.__init__ or not \ + (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): + return None + try: + argspec = inspect.getargspec(initmeth) + except TypeError: + # still not possible: happens e.g. for old-style classes + # with __init__ in C + return None + if argspec[0] and argspec[0][0] in ('cls', 'self'): + del argspec[0][0] + return inspect.formatargspec(*argspec) + + def format_signature(self): + if self.doc_as_attr: + return '' + return ModuleLevelDocumenter.format_signature(self) - if self.options.show_inheritance and what in ('class', 'exception'): - if len(todoc.__bases__): + def add_directive_header(self, sig): + if self.doc_as_attr: + self.directivetype = 'attribute' + Documenter.add_directive_header(self, sig) + + # add inheritance info, if wanted + if not self.doc_as_attr and self.options.show_inheritance: + self.add_line(u'', '<autodoc>') + if len(self.object.__bases__): bases = [b.__module__ == '__builtin__' and u':class:`%s`' % b.__name__ or u':class:`%s.%s`' % (b.__module__, b.__name__) - for b in todoc.__bases__] - self.result.append(indent + u' Bases: %s' % ', '.join(bases), - '<autodoc>') - self.result.append(u'', '<autodoc>') + for b in self.object.__bases__] + self.add_line(_(u' Bases: %s') % ', '.join(bases), + '<autodoc>') - # the module directive doesn't have content - if what != 'module': - indent += u' ' + def get_doc(self, encoding=None): + content = self.env.config.autoclass_content - if modfile: - sourcename = '%s:docstring of %s' % (modfile, fullname) - else: - sourcename = 'docstring of %s' % fullname + docstrings = [] + docstring = self.get_attr(self.object, '__doc__', None) + if docstring: + docstrings.append(docstring) + + # for classes, what the "docstring" is can be controlled via a + # config value; the default is only the class docstring + if content in ('both', 'init'): + initdocstring = self.get_attr( + self.get_attr(self.object, '__init__', None), '__doc__') + # for new-style classes, no __init__ means default __init__ + if initdocstring == object.__init__.__doc__: + initdocstring = None + if initdocstring: + if content == 'init': + docstrings = [initdocstring] + else: + docstrings.append(initdocstring) - # add content from docstrings - for i, line in enumerate(self.get_doc(what, fullname, todoc)): - self.result.append(indent + line, sourcename, i) + return [prepare_docstring(force_decode(docstring, encoding)) + for docstring in docstrings] - # add source content, if present - if add_content: - for line, src in zip(add_content.data, add_content.items): - self.result.append(indent + line, src[0], src[1]) + def add_content(self, more_content, no_docstring=False): + if self.doc_as_attr: + content = ViewList( + [_('alias of :class:`%s`') % self.object.__name__], source='') + ModuleLevelDocumenter.add_content(self, content, no_docstring=True) + else: + ModuleLevelDocumenter.add_content(self, more_content) - # document members? - if not members or what in ('function', 'method', 'attribute'): + def document_members(self, all_members=False): + if self.doc_as_attr: return + ModuleLevelDocumenter.document_members(self, all_members) - # set current namespace for finding members - self.env.autodoc_current_module = mod - if objpath: - self.env.autodoc_current_class = objpath[0] - - # add members, if possible - _all = members == ['__all__'] - members_check_module = False - if _all: - # unqualified :members: given - if what == 'module': - if hasattr(todoc, '__all__'): - members_check_module = False - all_members = [] - for mname in todoc.__all__: - try: - all_members.append((mname, getattr(todoc, mname))) - except AttributeError: - self.warn('missing attribute mentioned in __all__: ' - 'module %s, attribute %s' % - (todoc.__name__, mname)) - else: - # for implicit module members, check __module__ to avoid - # documenting imported objects - members_check_module = True - all_members = inspect.getmembers(todoc) - else: - if self.options.inherited_members: - # getmembers() uses dir() which pulls in members from all - # base classes - all_members = inspect.getmembers(todoc) - else: - # __dict__ contains only the members directly defined in the class - all_members = sorted(todoc.__dict__.iteritems()) + +class ExceptionDocumenter(ClassDocumenter): + """ + Specialized ClassDocumenter subclass for exceptions. + """ + objtype = 'exception' + member_order = 10 + + # needs a higher priority than ClassDocumenter + priority = 10 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return isinstance(member, (type, ClassType)) and \ + issubclass(member, base_exception) + + +class DataDocumenter(ModuleLevelDocumenter): + """ + Specialized Documenter subclass for data items. + """ + objtype = 'data' + member_order = 40 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return isinstance(parent, ModuleDocumenter) and isattr + + def document_members(self, all_members=False): + pass + + +class MethodDocumenter(ClassLevelDocumenter): + """ + Specialized Documenter subclass for methods (normal, static and class). + """ + objtype = 'method' + member_order = 50 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + # other attributes are recognized via the module analyzer + return inspect.isroutine(member) and \ + not isinstance(parent, ModuleDocumenter) + + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + if isinstance(self.object, classmethod) or \ + (isinstance(self.object, MethodType) and + self.object.im_self is not None): + self.directivetype = 'classmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 + elif isinstance(self.object, FunctionType) or \ + (isinstance(self.object, BuiltinFunctionType) and + self.object.__self__ is not None): + self.directivetype = 'staticmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 else: - all_members = [(mname, getattr(todoc, mname)) for mname in members] - for (membername, member) in all_members: - # ignore members whose name starts with _ by default - if _all and membername.startswith('_'): - continue + self.directivetype = 'method' + return ret - # ignore undocumented members if :undoc-members: is not given - doc = getattr(member, '__doc__', None) - skip = not self.options.undoc_members and not doc - # give the user a chance to decide whether this member should be skipped - if self.env.app: - # let extensions preprocess docstrings - skip_user = self.env.app.emit_firstresult( - 'autodoc-skip-member', what, membername, member, skip, self.options) - if skip_user is not None: - skip = skip_user - if skip: - continue + def format_args(self): + if inspect.isbuiltin(self.object) or \ + inspect.ismethoddescriptor(self.object): + # can never get arguments of a C function or method + return None + argspec = inspect.getargspec(self.object) + if argspec[0] and argspec[0][0] in ('cls', 'self'): + del argspec[0][0] + return inspect.formatargspec(*argspec) - if what == 'module': - if isinstance(member, (types.FunctionType, - types.BuiltinFunctionType)): - memberwhat = 'function' - elif isinstance(member, types.ClassType) or \ - isinstance(member, type): - if issubclass(member, base_exception): - memberwhat = 'exception' - else: - memberwhat = 'class' - else: - # XXX: todo -- attribute docs - continue - else: - if callable(member): - memberwhat = 'method' - elif isdescriptor(member): - memberwhat = 'attribute' - else: - # XXX: todo -- attribute docs - continue - full_membername = fullname + '.' + membername - self.generate(memberwhat, full_membername, ['__all__'], None, indent, - check_module=members_check_module) + def document_members(self, all_members=False): + pass - self.env.autodoc_current_module = None - self.env.autodoc_current_class = None +class AttributeDocumenter(ClassLevelDocumenter): + """ + Specialized Documenter subclass for attributes. + """ + objtype = 'attribute' + member_order = 60 -def _auto_directive(dirname, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - what = dirname[4:] # strip "auto" - name = arguments[0] - genopt = Options() - members = options.get('members', []) - genopt.inherited_members = 'inherited-members' in options - if genopt.inherited_members and not members: - # :inherited-members: implies :members: - members = ['__all__'] - genopt.undoc_members = 'undoc-members' in options - genopt.show_inheritance = 'show-inheritance' in options - genopt.noindex = 'noindex' in options - genopt.synopsis = options.get('synopsis', '') - genopt.platform = options.get('platform', '') - genopt.deprecated = 'deprecated' in options - - generator = RstGenerator(genopt, state.document, lineno) - generator.generate(what, name, members, content) - if not generator.result: - return generator.warnings - - # record all filenames as dependencies -- this will at least partially make - # automatic invalidation possible - for fn in generator.filename_set: - state.document.settings.env.note_dependency(fn) - - # use a custom reporter that correctly assigns lines to source and lineno - old_reporter = state.memo.reporter - state.memo.reporter = AutodocReporter(generator.result, state.memo.reporter) - if dirname == 'automodule': - node = nodes.section() - nested_parse_with_titles(state, generator.result, node) - else: - node = nodes.paragraph() - state.nested_parse(generator.result, 0, node) - state.memo.reporter = old_reporter - return generator.warnings + node.children - -def auto_directive(*args, **kwds): - return _auto_directive(*args, **kwds) - -def automodule_directive(*args, **kwds): - return _auto_directive(*args, **kwds) - -def autoclass_directive(*args, **kwds): - return _auto_directive(*args, **kwds) + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return (isdescriptor(member) and not + isinstance(member, (FunctionType, BuiltinFunctionType))) \ + or (not isinstance(parent, ModuleDocumenter) and isattr) + def document_members(self, all_members=False): + pass -def members_option(arg): - if arg is None: - return ['__all__'] - return [x.strip() for x in arg.split(',')] + +class AutoDirective(Directive): + """ + The AutoDirective class is used for all autodoc directives. It dispatches + most of the work to one of the Documenters, which it selects through its + *_registry* dictionary. + + The *_special_attrgetters* attribute is used to customize ``getattr()`` + calls that the Documenters make; its entries are of the form ``type: + getattr_function``. + + Note: When importing an object, all items along the import chain are + accessed using the descendant's *_special_attrgetters*, thus this + dictionary should include all necessary functions for accessing + attributes of the parents. + """ + # a registry of objtype -> documenter class + _registry = {} + + # a registry of type -> getattr function + _special_attrgetters = {} + + # standard docutils directive settings + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + # allow any options to be passed; the options are parsed further + # by the selected Documenter + option_spec = DefDict(identity) + + def warn(self, msg): + self.warnings.append(self.reporter.warning(msg, line=self.lineno)) + + def run(self): + self.filename_set = set() # a set of dependent filenames + self.reporter = self.state.document.reporter + self.env = self.state.document.settings.env + self.warnings = [] + self.result = ViewList() + + # find out what documenter to call + objtype = self.name[4:] + doc_class = self._registry[objtype] + # process the options with the selected documenter's option_spec + self.genopt = Options(assemble_option_dict( + self.options.items(), doc_class.option_spec)) + # generate the output + documenter = doc_class(self, self.arguments[0]) + documenter.generate(more_content=self.content) + if not self.result: + return self.warnings + + # record all filenames as dependencies -- this will at least + # partially make automatic invalidation possible + for fn in self.filename_set: + self.env.note_dependency(fn) + + # use a custom reporter that correctly assigns lines to source + # filename/description and lineno + old_reporter = self.state.memo.reporter + self.state.memo.reporter = AutodocReporter(self.result, + self.state.memo.reporter) + + if self.name == 'automodule': + node = nodes.section() + # necessary so that the child nodes get the right source/line set + node.document = self.state.document + nested_parse_with_titles(self.state, self.result, node) + else: + node = nodes.paragraph() + node.document = self.state.document + self.state.nested_parse(self.result, 0, node) + self.state.memo.reporter = old_reporter + return self.warnings + node.children + + +def add_documenter(cls): + """Register a new Documenter.""" + if not issubclass(cls, Documenter): + raise ExtensionError('autodoc documenter %r must be a subclass ' + 'of Documenter' % cls) + # actually, it should be possible to override Documenters + #if cls.objtype in AutoDirective._registry: + # raise ExtensionError('autodoc documenter for %r is already ' + # 'registered' % cls.objtype) + AutoDirective._registry[cls.objtype] = cls def setup(app): - mod_options = {'members': members_option, 'undoc-members': directives.flag, - 'noindex': directives.flag, 'inherited-members': directives.flag, - 'show-inheritance': directives.flag, 'synopsis': lambda x: x, - 'platform': lambda x: x, 'deprecated': directives.flag} - cls_options = {'members': members_option, 'undoc-members': directives.flag, - 'noindex': directives.flag, 'inherited-members': directives.flag, - 'show-inheritance': directives.flag} - app.add_directive('automodule', automodule_directive, - 1, (1, 0, 1), **mod_options) - app.add_directive('autoclass', autoclass_directive, - 1, (1, 0, 1), **cls_options) - app.add_directive('autoexception', autoclass_directive, - 1, (1, 0, 1), **cls_options) - app.add_directive('autofunction', auto_directive, 1, (1, 0, 1), - noindex=directives.flag) - app.add_directive('automethod', auto_directive, 1, (1, 0, 1), - noindex=directives.flag) - app.add_directive('autoattribute', auto_directive, 1, (1, 0, 1), - noindex=directives.flag) - # deprecated: remove in some future version. - app.add_config_value('automodule_skip_lines', 0, True) + app.add_autodocumenter(ModuleDocumenter) + app.add_autodocumenter(ClassDocumenter) + app.add_autodocumenter(ExceptionDocumenter) + app.add_autodocumenter(DataDocumenter) + app.add_autodocumenter(FunctionDocumenter) + app.add_autodocumenter(MethodDocumenter) + app.add_autodocumenter(AttributeDocumenter) + app.add_config_value('autoclass_content', 'class', True) + app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py new file mode 100644 index 00000000..3d260fd2 --- /dev/null +++ b/sphinx/ext/autosummary/__init__.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +""" + sphinx.ext.autosummary + ~~~~~~~~~~~~~~~~~~~~~~ + + Sphinx extension that adds an autosummary:: directive, which can be + used to generate function/method/attribute/etc. summary lists, similar + to those output eg. by Epydoc and other API doc generation tools. + + An :autolink: role is also provided. + + autosummary directive + --------------------- + + The autosummary directive has the form:: + + .. autosummary:: + :nosignatures: + :toctree: generated/ + + module.function_1 + module.function_2 + ... + + and it generates an output table (containing signatures, optionally) + + ======================== ============================================= + module.function_1(args) Summary line from the docstring of function_1 + module.function_2(args) Summary line from the docstring + ... + ======================== ============================================= + + If the :toctree: option is specified, files matching the function names + are inserted to the toctree with the given prefix: + + generated/module.function_1 + generated/module.function_2 + ... + + Note: The file names contain the module:: or currentmodule:: prefixes. + + .. seealso:: autosummary_generate.py + + + autolink role + ------------- + + The autolink role functions as ``:obj:`` when the name referred can be + resolved to a Python object, and otherwise it becomes simple emphasis. + This can be used as the default role to make links 'smart'. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import sys +import inspect +import posixpath +from os import path + +from docutils.parsers.rst import directives +from docutils.statemachine import ViewList +from docutils import nodes + +from sphinx import addnodes, roles +from sphinx.util import patfilter +from sphinx.util.compat import Directive + + +# -- autosummary_toc node ------------------------------------------------------ + +class autosummary_toc(nodes.comment): + pass + +def process_autosummary_toc(app, doctree): + """ + Insert items described in autosummary:: to the TOC tree, but do + not generate the toctree:: list. + """ + env = app.builder.env + crawled = {} + def crawl_toc(node, depth=1): + crawled[node] = True + for j, subnode in enumerate(node): + try: + if (isinstance(subnode, autosummary_toc) + and isinstance(subnode[0], addnodes.toctree)): + env.note_toctree(env.docname, subnode[0]) + continue + except IndexError: + continue + if not isinstance(subnode, nodes.section): + continue + if subnode not in crawled: + crawl_toc(subnode, depth+1) + crawl_toc(doctree) + +def autosummary_toc_visit_html(self, node): + """Hide autosummary toctree list in HTML output.""" + raise nodes.SkipNode + +def autosummary_toc_visit_latex(self, node): + """Show autosummary toctree (= put the referenced pages here) in Latex.""" + pass + +def autosummary_noop(self, node): + pass + + +# -- .. autosummary:: ---------------------------------------------------------- + +class Autosummary(Directive): + """ + Pretty table containing short signatures and summaries of functions etc. + + autosummary also generates a (hidden) toctree:: node. + """ + + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + has_content = True + option_spec = { + 'toctree': directives.unchanged, + 'nosignatures': directives.flag, + } + + def run(self): + names = [] + names += [x.strip() for x in self.content if x.strip()] + + table, warnings, real_names = get_autosummary( + names, self.state, 'nosignatures' in self.options) + node = table + + env = self.state.document.settings.env + suffix = env.config.source_suffix + all_docnames = env.found_docs.copy() + dirname = posixpath.dirname(env.docname) + + if 'toctree' in self.options: + tree_prefix = self.options['toctree'].strip() + docnames = [] + for name in names: + name = real_names.get(name, name) + + docname = posixpath.join(tree_prefix, name) + if docname.endswith(suffix): + docname = docname[:-len(suffix)] + docname = posixpath.normpath(posixpath.join(dirname, docname)) + if docname not in env.found_docs: + warnings.append(self.state.document.reporter.warning( + 'toctree references unknown document %r' % docname, + line=self.lineno)) + docnames.append(docname) + + tocnode = addnodes.toctree() + tocnode['includefiles'] = docnames + tocnode['entries'] = [(None, docname) for docname in docnames] + tocnode['maxdepth'] = -1 + tocnode['glob'] = None + + tocnode = autosummary_toc('', '', tocnode) + return warnings + [node] + [tocnode] + else: + return warnings + [node] + + +def get_autosummary(names, state, no_signatures=False): + """ + Generate a proper table node for autosummary:: directive. + + *names* is a list of names of Python objects to be imported and added to the + table. *document* is the Docutils document object. + + """ + document = state.document + + real_names = {} + warnings = [] + + prefixes = [''] + prefixes.insert(0, document.settings.env.currmodule) + + table = nodes.table('') + group = nodes.tgroup('', cols=2) + table.append(group) + group.append(nodes.colspec('', colwidth=30)) + group.append(nodes.colspec('', colwidth=70)) + body = nodes.tbody('') + group.append(body) + + def append_row(*column_texts): + row = nodes.row('') + for text in column_texts: + node = nodes.paragraph('') + vl = ViewList() + vl.append(text, '<autosummary>') + state.nested_parse(vl, 0, node) + row.append(nodes.entry('', node)) + body.append(row) + + for name in names: + try: + obj, real_name = import_by_name(name, prefixes=prefixes) + except ImportError: + warnings.append(document.reporter.warning( + 'failed to import %s' % name)) + append_row(':obj:`%s`' % name, '') + continue + + real_names[name] = real_name + + title = '' + qualifier = 'obj' + col1 = ':'+qualifier+':`%s <%s>`' % (name, real_name) + col2 = title + append_row(col1, col2) + + return table, warnings, real_names + +def import_by_name(name, prefixes=[None]): + """ + Import a Python object that has the given *name*, under one of the + *prefixes*. The first name that succeeds is used. + """ + tried = [] + for prefix in prefixes: + try: + if prefix: + prefixed_name = '.'.join([prefix, name]) + else: + prefixed_name = name + return _import_by_name(prefixed_name), prefixed_name + except ImportError: + tried.append(prefixed_name) + raise ImportError('no module named %s' % ' or '.join(tried)) + +def _import_by_name(name): + """Import a Python object given its full name.""" + try: + # try first interpret `name` as MODNAME.OBJ + name_parts = name.split('.') + try: + modname = '.'.join(name_parts[:-1]) + __import__(modname) + return getattr(sys.modules[modname], name_parts[-1]) + except (ImportError, IndexError, AttributeError): + pass + + # ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ... + last_j = 0 + modname = None + for j in reversed(range(1, len(name_parts)+1)): + last_j = j + modname = '.'.join(name_parts[:j]) + try: + __import__(modname) + except ImportError: + continue + if modname in sys.modules: + break + + if last_j < len(name_parts): + obj = sys.modules[modname] + for obj_name in name_parts[last_j:]: + obj = getattr(obj, obj_name) + return obj + else: + return sys.modules[modname] + except (ValueError, ImportError, AttributeError, KeyError), e: + raise ImportError(*e.args) + + +# -- :autolink: (smart default role) ------------------------------------------- + +def autolink_role(typ, rawtext, etext, lineno, inliner, + options={}, content=[]): + """ + Smart linking role. + + Expands to ':obj:`text`' if `text` is an object that can be imported; + otherwise expands to '*text*'. + """ + r = roles.xfileref_role('obj', rawtext, etext, lineno, inliner, + options, content) + pnode = r[0][0] + + prefixes = [None] + #prefixes.insert(0, inliner.document.settings.env.currmodule) + try: + obj, name = import_by_name(pnode['reftarget'], prefixes) + except ImportError: + content = pnode[0] + r[0][0] = nodes.emphasis(rawtext, content[0].astext(), + classes=content['classes']) + return r + + +def process_generate_options(app): + genfiles = app.config.autosummary_generate + if not genfiles: + return + from sphinx.ext.autosummary.generate import generate_autosummary_docs + + ext = app.config.source_suffix + genfiles = [path.join(app.srcdir, genfile + + (not genfile.endswith(ext) and ext or '')) + for genfile in genfiles] + generate_autosummary_docs(genfiles, warn=app.warn, info=app.info, + suffix=ext) + + +def setup(app): + # I need autodoc + app.setup_extension('sphinx.ext.autodoc') + app.add_node(autosummary_toc, + html=(autosummary_toc_visit_html, autosummary_noop), + latex=(autosummary_toc_visit_latex, autosummary_noop), + text=(autosummary_noop, autosummary_noop)) + app.add_directive('autosummary', Autosummary) + app.add_role('autolink', autolink_role) + app.connect('doctree-read', process_autosummary_toc) + app.connect('builder-inited', process_generate_options) + app.add_config_value('autosummary_generate', [], True) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py new file mode 100644 index 00000000..765b88ec --- /dev/null +++ b/sphinx/ext/autosummary/generate.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +""" + sphinx.ext.autosummary.generate + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Usable as a library or script to generate automatic RST source files for + items referred to in autosummary:: directives. + + Each generated RST file contains a single auto*:: directive which + extracts the docstring of the referred item. + + Example Makefile rule:: + + generate: + sphinx-autogen source/*.rst source/generated + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os +import re +import sys +import getopt +import inspect + +from jinja2 import Environment, PackageLoader + +from sphinx.ext.autosummary import import_by_name +from sphinx.util import ensuredir + +# create our own templating environment, for module template only +env = Environment(loader=PackageLoader('sphinx.ext.autosummary', 'templates')) + + +def _simple_info(msg): + print msg + +def _simple_warn(msg): + print >>sys.stderr, 'WARNING: ' + msg + +def generate_autosummary_docs(sources, output_dir=None, suffix=None, + warn=_simple_warn, info=_simple_info): + info('generating autosummary for: %s' % ', '.join(sources)) + if output_dir: + info('writing to %s' % output_dir) + # read + names = {} + for name, loc in get_documented(sources).items(): + for (filename, sec_title, keyword, toctree) in loc: + if toctree is not None: + path = os.path.join(os.path.dirname(filename), toctree) + names[name] = os.path.abspath(path) + + # write + for name, path in sorted(names.items()): + path = output_dir or path + ensuredir(path) + + try: + obj, name = import_by_name(name) + except ImportError, e: + warn('failed to import %r: %s' % (name, e)) + continue + + fn = os.path.join(path, name + (suffix or '.rst')) + # skip it if it exists + if os.path.isfile(fn): + continue + + f = open(fn, 'w') + + try: + if inspect.ismodule(obj): + # XXX replace this with autodoc's API? + tmpl = env.get_template('module') + functions = [getattr(obj, item).__name__ + for item in dir(obj) + if inspect.isfunction(getattr(obj, item))] + classes = [getattr(obj, item).__name__ + for item in dir(obj) + if inspect.isclass(getattr(obj, item)) + and not issubclass(getattr(obj, item), Exception)] + exceptions = [getattr(obj, item).__name__ + for item in dir(obj) + if inspect.isclass(getattr(obj, item)) + and issubclass(getattr(obj, item), Exception)] + rendered = tmpl.render(name=name, + underline='='*len(name), + functions=functions, + classes=classes, + exceptions=exceptions, + len_functions=len(functions), + len_classes=len(classes), + len_exceptions=len(exceptions)) + f.write(rendered) + else: + f.write('%s\n%s\n\n' % (name, '='*len(name))) + + if inspect.isclass(obj): + if issubclass(obj, Exception): + f.write(format_modulemember(name, 'autoexception')) + else: + f.write(format_modulemember(name, 'autoclass')) + elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): + f.write(format_classmember(name, 'automethod')) + elif callable(obj): + f.write(format_modulemember(name, 'autofunction')) + elif hasattr(obj, '__get__'): + f.write(format_classmember(name, 'autoattribute')) + else: + f.write(format_modulemember(name, 'autofunction')) + finally: + f.close() + + +def format_modulemember(name, directive): + parts = name.split('.') + mod, name = '.'.join(parts[:-1]), parts[-1] + return '.. currentmodule:: %s\n\n.. %s:: %s\n' % (mod, directive, name) + + +def format_classmember(name, directive): + parts = name.split('.') + mod, name = '.'.join(parts[:-2]), '.'.join(parts[-2:]) + return '.. currentmodule:: %s\n\n.. %s:: %s\n' % (mod, directive, name) + + +title_underline_re = re.compile('^[-=*_^#]{3,}\s*$') +autodoc_re = re.compile(r'.. auto(function|method|attribute|class|exception' + '|module)::\s*([A-Za-z0-9_.]+)\s*$') +autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*') +module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$') +autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*') +toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$') + +def get_documented(filenames): + """ + Find out what items are documented in the given filenames. + + Returns a dict of list of (filename, title, keyword, toctree) Keys are + documented names of objects. The value is a list of locations where the + object was documented. Each location is a tuple of filename, the current + section title, the name of the directive, and the value of the :toctree: + argument (if present) of the directive. + """ + + documented = {} + + for filename in filenames: + current_title = [] + last_line = None + toctree = None + current_module = None + in_autosummary = False + + f = open(filename, 'r') + for line in f: + try: + if in_autosummary: + m = toctree_arg_re.match(line) + if m: + toctree = m.group(1) + continue + + if line.strip().startswith(':'): + continue # skip options + + m = autosummary_item_re.match(line) + + if m: + name = m.group(1).strip() + if current_module and \ + not name.startswith(current_module + '.'): + name = '%s.%s' % (current_module, name) + documented.setdefault(name, []).append( + (filename, current_title, 'autosummary', toctree)) + continue + if line.strip() == '': + continue + in_autosummary = False + + m = autosummary_re.match(line) + if m: + in_autosummary = True + continue + + m = autodoc_re.search(line) + if m: + name = m.group(2).strip() + # XXX look in newer generate.py + if current_module and \ + not name.startswith(current_module + '.'): + name = '%s.%s' % (current_module, name) + if m.group(1) == 'module': + current_module = name + documented.setdefault(name, []).append( + (filename, current_title, 'auto' + m.group(1), None)) + continue + + m = title_underline_re.match(line) + if m and last_line: + current_title = last_line.strip() + continue + + m = module_re.match(line) + if m: + current_module = m.group(2) + continue + finally: + last_line = line + return documented + + +def main(argv): + usage = 'usage: %s [-o output_dir] [-s suffix] sourcefile ...' % sys.argv[0] + try: + opts, args = getopt.getopt(argv[1:], 'o:s:') + except getopt.error: + print >>sys.stderr, usage + return 1 + + output_dir = None + suffix = None + for opt, val in opts: + if opt == '-o': + output_dir = val + elif opt == '-s': + suffix = val + + if len(args) < 1: + print >>sys.stderr, usage + return 1 + + generate_autosummary_docs(args, output_dir, suffix) + + +if __name__ == '__main__': + main() diff --git a/sphinx/ext/autosummary/templates/module b/sphinx/ext/autosummary/templates/module new file mode 100644 index 00000000..0cbc8266 --- /dev/null +++ b/sphinx/ext/autosummary/templates/module @@ -0,0 +1,39 @@ +:mod:`{{name}}` +======{{ underline }}= + + +.. automodule:: {{name}} + +{% if len_functions > 0 %} +Functions +---------- +{% for item in functions %} +.. autofunction:: {{item}} +{% endfor %} +{% endif %} + +{% if len_classes > 0 %} +Classes +-------- +{% for item in classes %} +.. autoclass:: {{item}} + :show-inheritance: + :members: + :inherited-members: + :undoc-members: + +{% endfor %} +{% endif %} + +{% if len_exceptions > 0 %} +Exceptions +------------ +{% for item in exceptions %} +.. autoclass:: {{item}} + :show-inheritance: + :members: + :inherited-members: + :undoc-members: + +{% endfor %} +{% endif %} diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index c26a1cfd..964e58ee 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -16,7 +16,7 @@ import inspect import cPickle as pickle from os import path -from sphinx.builder import Builder +from sphinx.builders import Builder # utility @@ -53,17 +53,17 @@ class CoverageBuilder(Builder): self.c_ignorexps = {} for (name, exps) in self.config.coverage_ignore_c_items.iteritems(): - self.c_ignorexps[name] = compile_regex_list('coverage_ignore_c_items', - exps, self.warn) - self.mod_ignorexps = compile_regex_list('coverage_ignore_modules', - self.config.coverage_ignore_modules, - self.warn) - self.cls_ignorexps = compile_regex_list('coverage_ignore_classes', - self.config.coverage_ignore_classes, - self.warn) - self.fun_ignorexps = compile_regex_list('coverage_ignore_functions', - self.config.coverage_ignore_functions, - self.warn) + self.c_ignorexps[name] = compile_regex_list( + 'coverage_ignore_c_items', exps, self.warn) + self.mod_ignorexps = compile_regex_list( + 'coverage_ignore_modules', self.config.coverage_ignore_modules, + self.warn) + self.cls_ignorexps = compile_regex_list( + 'coverage_ignore_classes', self.config.coverage_ignore_classes, + self.warn) + self.fun_ignorexps = compile_regex_list( + 'coverage_ignore_functions', self.config.coverage_ignore_functions, + self.warn) def get_outdated_docs(self): return 'coverage overview' @@ -128,7 +128,8 @@ class CoverageBuilder(Builder): try: mod = __import__(mod_name, fromlist=['foo']) except ImportError, err: - self.warn('module %s could not be imported: %s' % (mod_name, err)) + self.warn('module %s could not be imported: %s' % + (mod_name, err)) self.py_undoc[mod_name] = {'error': err} continue @@ -168,7 +169,8 @@ class CoverageBuilder(Builder): attrs = [] - for attr_name, attr in inspect.getmembers(obj, inspect.ismethod): + for attr_name, attr in inspect.getmembers( + obj, inspect.ismethod): if attr_name[0] == '_': # starts with an underscore, ignore it continue diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 3a44c3f7..51463661 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -22,7 +22,8 @@ doctest = __import__('doctest') from docutils import nodes from docutils.parsers.rst import directives -from sphinx.builder import Builder +from sphinx.builders import Builder +from sphinx.util.compat import Directive from sphinx.util.console import bold blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) @@ -30,63 +31,77 @@ doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) # set up the necessary directives -def test_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # use ordinary docutils nodes for test code: they get special attributes - # so that our builder recognizes them, and the other builders are happy. - code = '\n'.join(content) - test = None - if name == 'doctest': - if '<BLANKLINE>' in code: - # convert <BLANKLINE>s to ordinary blank lines for presentation - test = code - code = blankline_re.sub('', code) - if doctestopt_re.search(code): - if not test: - test = code - code = doctestopt_re.sub('', code) - nodetype = nodes.literal_block - if name == 'testsetup' or 'hide' in options: - nodetype = nodes.comment - if arguments: - groups = [x.strip() for x in arguments[0].split(',')] - else: - groups = ['default'] - node = nodetype(code, code, testnodetype=name, groups=groups) - node.line = lineno - if test is not None: - # only save if it differs from code - node['test'] = test - if name == 'testoutput': - # don't try to highlight output - node['language'] = 'none' - node['options'] = {} - if name in ('doctest', 'testoutput') and 'options' in options: - # parse doctest-like output comparison flags - option_strings = options['options'].replace(',', ' ').split() - for option in option_strings: - if (option[0] not in '+-' or option[1:] not in - doctest.OPTIONFLAGS_BY_NAME): - # XXX warn? - continue - flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] - node['options'][flag] = (option[0] == '+') - return [node] +class TestDirective(Directive): + """ + Base class for doctest-related directives. + """ -# need to have individual functions for each directive due to different -# options they accept + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + + def run(self): + # use ordinary docutils nodes for test code: they get special attributes + # so that our builder recognizes them, and the other builders are happy. + code = '\n'.join(self.content) + test = None + if self.name == 'doctest': + if '<BLANKLINE>' in code: + # convert <BLANKLINE>s to ordinary blank lines for presentation + test = code + code = blankline_re.sub('', code) + if doctestopt_re.search(code): + if not test: + test = code + code = doctestopt_re.sub('', code) + nodetype = nodes.literal_block + if self.name == 'testsetup' or 'hide' in self.options: + nodetype = nodes.comment + if self.arguments: + groups = [x.strip() for x in self.arguments[0].split(',')] + else: + groups = ['default'] + node = nodetype(code, code, testnodetype=self.name, groups=groups) + node.line = self.lineno + if test is not None: + # only save if it differs from code + node['test'] = test + if self.name == 'testoutput': + # don't try to highlight output + node['language'] = 'none' + node['options'] = {} + if self.name in ('doctest', 'testoutput') and 'options' in self.options: + # parse doctest-like output comparison flags + option_strings = self.options['options'].replace(',', ' ').split() + for option in option_strings: + if (option[0] not in '+-' or option[1:] not in + doctest.OPTIONFLAGS_BY_NAME): + # XXX warn? + continue + flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] + node['options'][flag] = (option[0] == '+') + return [node] -def testsetup_directive(*args): - return test_directive(*args) +class TestsetupDirective(TestDirective): + option_spec = {} -def doctest_directive(*args): - return test_directive(*args) +class DoctestDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + 'options': directives.unchanged, + } -def testcode_directive(*args): - return test_directive(*args) +class TestcodeDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + } -def testoutput_directive(*args): - return test_directive(*args) +class TestoutputDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + 'options': directives.unchanged, + } parser = doctest.DocTestParser() @@ -99,9 +114,12 @@ class TestGroup(object): self.setup = [] self.tests = [] - def add_code(self, code): + def add_code(self, code, prepend=False): if code.type == 'testsetup': - self.setup.append(code) + if prepend: + self.setup.insert(0, code) + else: + self.setup.append(code) elif code.type == 'doctest': self.tests.append([code]) elif code.type == 'testcode': @@ -258,10 +276,16 @@ Doctest summary for code in add_to_all_groups: for group in groups.itervalues(): group.add_code(code) + if self.config.doctest_global_setup: + code = TestCode(self.config.doctest_global_setup, + 'testsetup', lineno=0) + for group in groups.itervalues(): + group.add_code(code, prepend=True) if not groups: return - self._out('\nDocument: %s\n----------%s\n' % (docname, '-'*len(docname))) + self._out('\nDocument: %s\n----------%s\n' % + (docname, '-'*len(docname))) for group in groups.itervalues(): self.test_group(group, self.env.doc2path(docname, base=None)) # Separately count results from setup code @@ -280,7 +304,8 @@ Doctest summary ns = {} examples = [] for setup in group.setup: - examples.append(doctest.Example(setup.code, '', lineno=setup.lineno)) + examples.append(doctest.Example(setup.code, '', + lineno=setup.lineno)) if examples: # simulate a doctest with the setup code setup_doctest = doctest.DocTest(examples, {}, @@ -324,14 +349,12 @@ Doctest summary def setup(app): - app.add_directive('testsetup', testsetup_directive, 1, (0, 1, 1)) - app.add_directive('doctest', doctest_directive, 1, (0, 1, 1), - hide=directives.flag, options=directives.unchanged) - app.add_directive('testcode', testcode_directive, 1, (0, 1, 1), - hide=directives.flag) - app.add_directive('testoutput', testoutput_directive, 1, (0, 1, 1), - hide=directives.flag, options=directives.unchanged) + app.add_directive('testsetup', TestsetupDirective) + app.add_directive('doctest', DoctestDirective) + app.add_directive('testcode', TestcodeDirective) + app.add_directive('testoutput', TestoutputDirective) app.add_builder(DocTestBuilder) # this config value adds to sys.path app.add_config_value('doctest_path', [], False) app.add_config_value('doctest_test_doctest_blocks', 'default', False) + app.add_config_value('doctest_global_setup', '', False) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py new file mode 100644 index 00000000..ea7b206d --- /dev/null +++ b/sphinx/ext/graphviz.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +""" + sphinx.ext.graphviz + ~~~~~~~~~~~~~~~~~~~ + + Allow graphviz-formatted graphs to be included in Sphinx-generated + documents inline. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import sys +import posixpath +from os import path +from subprocess import Popen, PIPE +try: + from hashlib import sha1 as sha +except ImportError: + from sha import sha + +from docutils import nodes + +from sphinx.errors import SphinxError +from sphinx.util import ensuredir +from sphinx.util.compat import Directive + + +mapname_re = re.compile(r'<map id="(.*?)"') + + +class GraphvizError(SphinxError): + category = 'Graphviz error' + + +class graphviz(nodes.General, nodes.Element): + pass + + +class Graphviz(Directive): + """ + Directive to insert arbitrary dot markup. + """ + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + node = graphviz() + node['code'] = '\n'.join(self.content) + node['options'] = [] + return [node] + + +class GraphvizSimple(Directive): + """ + Directive to insert arbitrary dot markup. + """ + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + node = graphviz() + node['code'] = '%s %s {\n%s\n}\n' % \ + (self.name, self.arguments[0], '\n'.join(self.content)) + node['options'] = [] + return [node] + + +def render_dot(self, code, options, format, prefix='graphviz'): + """ + Render graphviz code into a PNG or PDF output file. + """ + hashkey = code.encode('utf-8') + str(options) + \ + str(self.builder.config.graphviz_dot_args) + fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format) + if hasattr(self.builder, 'imgpath'): + # HTML + relfn = posixpath.join(self.builder.imgpath, fname) + outfn = path.join(self.builder.outdir, '_images', fname) + else: + # LaTeX + relfn = fname + outfn = path.join(self.builder.outdir, fname) + + if path.isfile(outfn): + return relfn, outfn + + if hasattr(self.builder, '_graphviz_warned_dot') or \ + hasattr(self.builder, '_graphviz_warned_ps2pdf'): + return None + + ensuredir(path.dirname(outfn)) + + dot_args = [self.builder.config.graphviz_dot] + dot_args.extend(self.builder.config.graphviz_dot_args) + dot_args.extend(options) + dot_args.extend(['-T' + format, '-o' + outfn]) + if format == 'png': + dot_args.extend(['-Tcmapx', '-o%s.map' % outfn]) + try: + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE) + except OSError, err: + if err.errno != 2: # No such file or directory + raise + self.builder.warn('dot command %r cannot be run (needed for graphviz ' + 'output), check the graphviz_dot setting' % + self.builder.config.graphviz_dot) + self.builder._graphviz_warned_dot = True + return None + # graphviz expects UTF-8 by default + if isinstance(code, unicode): + code = code.encode('utf-8') + stdout, stderr = p.communicate(code) + if p.returncode != 0: + raise GraphvizError('dot exited with error:\n[stderr]\n%s\n' + '[stdout]\n%s' % (stderr, stdout)) + return relfn, outfn + + +def render_dot_html(self, node, code, options, prefix='graphviz', imgcls=None): + try: + fname, outfn = render_dot(self, code, options, 'png', prefix) + except GraphvizError, exc: + self.builder.warn('dot code %r: ' % code + str(exc)) + raise nodes.SkipNode + + self.body.append(self.starttag(node, 'p', CLASS='graphviz')) + if fname is None: + self.body.append(self.encode(code)) + else: + mapfile = open(outfn + '.map', 'rb') + try: + imgmap = mapfile.readlines() + finally: + mapfile.close() + imgcss = imgcls and 'class="%s"' % imgcls or '' + if len(imgmap) == 2: + # nothing in image map (the lines are <map> and </map>) + self.body.append('<img src="%s" alt="%s" %s/>\n' % + (fname, self.encode(code).strip(), imgcss)) + else: + # has a map: get the name of the map and connect the parts + mapname = mapname_re.match(imgmap[0]).group(1) + self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' % + (fname, self.encode(code).strip(), + mapname, imgcss)) + self.body.extend(imgmap) + self.body.append('</p>\n') + raise nodes.SkipNode + + +def html_visit_graphviz(self, node): + render_dot_html(self, node, node['code'], node['options']) + + +def render_dot_latex(self, node, code, options, prefix='graphviz'): + try: + fname = render_dot(self, code, options, 'pdf', prefix) + except GraphvizError, exc: + self.builder.warn('dot code %r: ' % code + str(exc)) + raise nodes.SkipNode + + if fname is not None: + self.body.append('\\includegraphics{%s}' % fname) + raise nodes.SkipNode + + +def latex_visit_graphviz(self, node): + render_dot_latex(self, node, node['code'], node['options']) + +def setup(app): + app.add_node(graphviz, + html=(html_visit_graphviz, None), + latex=(latex_visit_graphviz, None)) + app.add_directive('graphviz', Graphviz) + app.add_directive('graph', GraphvizSimple) + app.add_directive('digraph', GraphvizSimple) + app.add_config_value('graphviz_dot', 'dot', 'html') + app.add_config_value('graphviz_dot_args', [], 'html') diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py index f622ec49..90cd2b2c 100644 --- a/sphinx/ext/ifconfig.py +++ b/sphinx/ext/ifconfig.py @@ -13,8 +13,8 @@ This stuff is only included in the built docs for unstable versions. The argument for ``ifconfig`` is a plain Python expression, evaluated in the - namespace of the project configuration (that is, all variables from ``conf.py`` - are available.) + namespace of the project configuration (that is, all variables from + ``conf.py`` are available.) :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -22,17 +22,27 @@ from docutils import nodes +from sphinx.util.compat import Directive + class ifconfig(nodes.Element): pass -def ifconfig_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - node = ifconfig() - node.line = lineno - node['expr'] = arguments[0] - state.nested_parse(content, content_offset, node) - return [node] +class IfConfig(Directive): + + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + node = ifconfig() + node.document = self.state.document + node.line = self.lineno + node['expr'] = self.arguments[0] + self.state.nested_parse(self.content, self.content_offset, node) + return [node] def process_ifconfig_nodes(app, doctree, docname): @@ -58,5 +68,5 @@ def process_ifconfig_nodes(app, doctree, docname): def setup(app): app.add_node(ifconfig) - app.add_directive('ifconfig', ifconfig_directive, 1, (1, 0, 1)) + app.add_directive('ifconfig', IfConfig) app.connect('doctree-resolved', process_ifconfig_nodes) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py new file mode 100644 index 00000000..8183359d --- /dev/null +++ b/sphinx/ext/inheritance_diagram.py @@ -0,0 +1,367 @@ +""" + sphinx.ext.inheritance_diagram + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Defines a docutils directive for inserting inheritance diagrams. + + Provide the directive with one or more classes or modules (separated + by whitespace). For modules, all of the classes in that module will + be used. + + Example:: + + Given the following classes: + + class A: pass + class B(A): pass + class C(A): pass + class D(B, C): pass + class E(B): pass + + .. inheritance-diagram: D E + + Produces a graph like the following: + + A + / \ + B C + / \ / + E D + + The graph is inserted as a PNG+image map into HTML and a PDF in + LaTeX. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import sys +import inspect +import subprocess +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +from docutils import nodes +from docutils.parsers.rst import directives + +from sphinx.roles import xfileref_role +from sphinx.ext.graphviz import render_dot_html, render_dot_latex +from sphinx.util.compat import Directive + + +class_sig_re = re.compile(r'''^([\w.]*\.)? # module names + (\w+) \s* $ # class/final module name + ''', re.VERBOSE) + + +class InheritanceException(Exception): + pass + + +class InheritanceGraph(object): + """ + Given a list of classes, determines the set of classes that they inherit + from all the way to the root "object", and then is able to generate a + graphviz dot graph from them. + """ + def __init__(self, class_names, currmodule, show_builtins=False): + """ + *class_names* is a list of child classes to show bases from. + + If *show_builtins* is True, then Python builtins will be shown + in the graph. + """ + self.class_names = class_names + self.classes = self._import_classes(class_names, currmodule) + self.all_classes = self._all_classes(self.classes) + if len(self.all_classes) == 0: + raise InheritanceException('No classes found for ' + 'inheritance diagram') + self.show_builtins = show_builtins + + def _import_class_or_module(self, name, currmodule): + """ + Import a class using its fully-qualified *name*. + """ + try: + path, base = class_sig_re.match(name).groups() + except ValueError: + raise InheritanceException('Invalid class or module %r specified ' + 'for inheritance diagram' % name) + + fullname = (path or '') + base + path = (path and path.rstrip('.') or '') + + # two possibilities: either it is a module, then import it + try: + module = __import__(fullname) + todoc = sys.modules[fullname] + except ImportError: + # else it is a class, then import the module + if not path: + if currmodule: + # try the current module + path = currmodule + else: + raise InheritanceException( + 'Could not import class %r specified for ' + 'inheritance diagram' % base) + try: + module = __import__(path) + todoc = getattr(sys.modules[path], base) + except (ImportError, AttributeError): + raise InheritanceException( + 'Could not import class or module %r specified for ' + 'inheritance diagram' % (path + '.' + base)) + + # If a class, just return it + if inspect.isclass(todoc): + return [todoc] + elif inspect.ismodule(todoc): + classes = [] + for cls in todoc.__dict__.values(): + if inspect.isclass(cls) and cls.__module__ == todoc.__name__: + classes.append(cls) + return classes + raise InheritanceException('%r specified for inheritance diagram is ' + 'not a class or module' % name) + + def _import_classes(self, class_names, currmodule): + """ + Import a list of classes. + """ + classes = [] + for name in class_names: + classes.extend(self._import_class_or_module(name, currmodule)) + return classes + + def _all_classes(self, classes): + """ + Return a list of all classes that are ancestors of *classes*. + """ + all_classes = {} + + def recurse(cls): + all_classes[cls] = None + for c in cls.__bases__: + if c not in all_classes: + recurse(c) + + for cls in classes: + recurse(cls) + + return all_classes.keys() + + def class_name(self, cls, parts=0): + """ + Given a class object, return a fully-qualified name. This + works for things I've tested in matplotlib so far, but may not + be completely general. + """ + module = cls.__module__ + if module == '__builtin__': + fullname = cls.__name__ + else: + fullname = '%s.%s' % (module, cls.__name__) + if parts == 0: + return fullname + name_parts = fullname.split('.') + return '.'.join(name_parts[-parts:]) + + def get_all_class_names(self): + """ + Get all of the class names involved in the graph. + """ + return [self.class_name(x) for x in self.all_classes] + + # These are the default attrs for graphviz + default_graph_attrs = { + 'rankdir': 'LR', + 'size': '"8.0, 12.0"', + } + default_node_attrs = { + 'shape': 'box', + 'fontsize': 10, + 'height': 0.25, + 'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, ' + 'Arial, Helvetica, sans', + 'style': '"setlinewidth(0.5)"', + } + default_edge_attrs = { + 'arrowsize': 0.5, + 'style': '"setlinewidth(0.5)"', + } + + def _format_node_attrs(self, attrs): + return ','.join(['%s=%s' % x for x in attrs.items()]) + + def _format_graph_attrs(self, attrs): + return ''.join(['%s=%s;\n' % x for x in attrs.items()]) + + def generate_dot(self, name, parts=0, urls={}, env=None, + graph_attrs={}, node_attrs={}, edge_attrs={}): + """ + Generate a graphviz dot graph from the classes that + were passed in to __init__. + + *name* is the name of the graph. + + *urls* is a dictionary mapping class names to HTTP URLs. + + *graph_attrs*, *node_attrs*, *edge_attrs* are dictionaries containing + key/value pairs to pass on as graphviz properties. + """ + g_attrs = self.default_graph_attrs.copy() + n_attrs = self.default_node_attrs.copy() + e_attrs = self.default_edge_attrs.copy() + g_attrs.update(graph_attrs) + n_attrs.update(node_attrs) + e_attrs.update(edge_attrs) + if env: + g_attrs.update(env.config.inheritance_graph_attrs) + n_attrs.update(env.config.inheritance_node_attrs) + e_attrs.update(env.config.inheritance_edge_attrs) + + res = [] + res.append('digraph %s {\n' % name) + res.append(self._format_graph_attrs(g_attrs)) + + for cls in self.all_classes: + if not self.show_builtins and cls in __builtins__.values(): + continue + + name = self.class_name(cls, parts) + + # Write the node + this_node_attrs = n_attrs.copy() + url = urls.get(self.class_name(cls)) + if url is not None: + this_node_attrs['URL'] = '"%s"' % url + res.append(' "%s" [%s];\n' % + (name, self._format_node_attrs(this_node_attrs))) + + # Write the edges + for base in cls.__bases__: + if not self.show_builtins and base in __builtins__.values(): + continue + + base_name = self.class_name(base, parts) + res.append(' "%s" -> "%s" [%s];\n' % + (base_name, name, + self._format_node_attrs(e_attrs))) + res.append('}\n') + return ''.join(res) + + +class inheritance_diagram(nodes.General, nodes.Element): + """ + A docutils node to use as a placeholder for the inheritance diagram. + """ + pass + + +class InheritanceDiagram(Directive): + """ + Run when the inheritance_diagram directive is first encountered. + """ + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = { + 'parts': directives.nonnegative_int, + } + + def run(self): + node = inheritance_diagram() + node.document = self.state.document + env = self.state.document.settings.env + class_names = self.arguments[0].split() + + # Create a graph starting with the list of classes + try: + graph = InheritanceGraph(class_names, env.currmodule) + except InheritanceException, err: + return [node.document.reporter.warning(err.args[0], + line=self.lineno)] + + # Create xref nodes for each target of the graph's image map and + # add them to the doc tree so that Sphinx can resolve the + # references to real URLs later. These nodes will eventually be + # removed from the doctree after we're done with them. + for name in graph.get_all_class_names(): + refnodes, x = xfileref_role( + 'class', ':class:`%s`' % name, name, 0, self.state) + node.extend(refnodes) + # Store the graph object so we can use it to generate the + # dot file later + node['graph'] = graph + # Store the original content for use as a hash + node['parts'] = self.options.get('parts', 0) + node['content'] = ' '.join(class_names) + return [node] + + +def get_graph_hash(node): + return md5(node['content'] + str(node['parts'])).hexdigest()[-10:] + + +def html_visit_inheritance_diagram(self, node): + """ + Output the graph for HTML. This will insert a PNG with clickable + image map. + """ + graph = node['graph'] + parts = node['parts'] + + graph_hash = get_graph_hash(node) + name = 'inheritance%s' % graph_hash + + # Create a mapping from fully-qualified class names to URLs. + urls = {} + for child in node: + if child.get('refuri') is not None: + urls[child['reftitle']] = child.get('refuri') + elif child.get('refid') is not None: + urls[child['reftitle']] = '#' + child.get('refid') + + dotcode = graph.generate_dot(name, parts, urls, env=self.builder.env) + render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance') + raise nodes.SkipNode + + +def latex_visit_inheritance_diagram(self, node): + """ + Output the graph for LaTeX. This will insert a PDF. + """ + graph = node['graph'] + parts = node['parts'] + + graph_hash = get_graph_hash(node) + name = 'inheritance%s' % graph_hash + + dotcode = graph.generate_dot(name, parts, urls, env=self.builder.env, + graph_attrs={'size': '"6.0,6.0"'}) + render_dot_latex(self, node, dotcode, [], 'inheritance') + raise nodes.SkipNode + + +def skip(self, node): + raise nodes.SkipNode + + +def setup(app): + app.setup_extension('sphinx.ext.graphviz') + app.add_node( + inheritance_diagram, + latex=(latex_visit_inheritance_diagram, None), + html=(html_visit_inheritance_diagram, None), + text=(skip, None)) + app.add_directive('inheritance-diagram', InheritanceDiagram) + app.add_config_value('inheritance_graph_attrs', {}, False), + app.add_config_value('inheritance_node_attrs', {}, False), + app.add_config_value('inheritance_edge_attrs', {}, False), diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 4938c041..5340a55e 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -31,7 +31,7 @@ from os import path from docutils import nodes -from sphinx.builder import INVENTORY_FILENAME +from sphinx.builders.html import INVENTORY_FILENAME def fetch_inventory(app, uri, inv): diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py index 1bea3304..e51af455 100644 --- a/sphinx/ext/jsmath.py +++ b/sphinx/ext/jsmath.py @@ -32,7 +32,8 @@ def html_visit_displaymath(self, node): if i == 0: # necessary to e.g. set the id property correctly if node['number']: - self.body.append('<span class="eqno">(%s)</span>' % node['number']) + self.body.append('<span class="eqno">(%s)</span>' % + node['number']) self.body.append(self.starttag(node, 'div', CLASS='math')) else: # but only once! diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index 12af089d..fea786e3 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -12,6 +12,8 @@ from docutils import nodes, utils from docutils.parsers.rst import directives +from sphinx.util.compat import Directive + class math(nodes.Inline, nodes.TextElement): pass @@ -45,22 +47,33 @@ def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]): node['docname'] = inliner.document.settings.env.docname return [node], [] -def math_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - latex = '\n'.join(content) - if arguments and arguments[0]: - latex = arguments[0] + '\n\n' + latex - node = displaymath() - node['latex'] = latex - node['label'] = options.get('label', None) - node['nowrap'] = 'nowrap' in options - node['docname'] = state.document.settings.env.docname - ret = [node] - if node['label']: - tnode = nodes.target('', '', ids=['equation-' + node['label']]) - state.document.note_explicit_target(tnode) - ret.insert(0, tnode) - return ret + +class MathDirective(Directive): + + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = { + 'label': directives.unchanged, + 'nowrap': directives.flag, + } + + def run(self): + latex = '\n'.join(self.content) + if self.arguments and self.arguments[0]: + latex = self.arguments[0] + '\n\n' + latex + node = displaymath() + node['latex'] = latex + node['label'] = self.options.get('label', None) + node['nowrap'] = 'nowrap' in self.options + node['docname'] = self.state.document.settings.env.docname + ret = [node] + if node['label']: + tnode = nodes.target('', '', ids=['equation-' + node['label']]) + self.state.document.note_explicit_target(tnode) + ret.insert(0, tnode) + return ret def latex_visit_math(self, node): @@ -134,6 +147,5 @@ def setup(app, htmlinlinevisitors, htmldisplayvisitors): html=(html_visit_eqref, html_depart_eqref)) app.add_role('math', math_role) app.add_role('eq', eq_role) - app.add_directive('math', math_directive, 1, (0, 1, 1), - label=directives.unchanged, nowrap=directives.flag) + app.add_directive('math', MathDirective) app.connect('doctree-resolved', number_equations) diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index 847083f0..745bdba4 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -22,9 +22,9 @@ except ImportError: from docutils import nodes +from sphinx.errors import SphinxError from sphinx.util import ensuredir from sphinx.util.png import read_png_depth, write_png_depth -from sphinx.application import SphinxError from sphinx.ext.mathbase import setup as mathbase_setup, wrap_displaymath class MathExtError(SphinxError): @@ -131,8 +131,8 @@ def render_math(self, math): stdout, stderr = p.communicate() if p.returncode != 0: - raise MathExtError('latex exited with error:\n[stderr]\n%s\n[stdout]\n%s' - % (stderr, stdout)) + raise MathExtError('latex exited with error:\n[stderr]\n%s\n' + '[stdout]\n%s' % (stderr, stdout)) ensuredir(path.dirname(outfn)) # use some standard dvipng arguments @@ -156,8 +156,8 @@ def render_math(self, math): return None, None stdout, stderr = p.communicate() if p.returncode != 0: - raise MathExtError('dvipng exited with error:\n[stderr]\n%s\n[stdout]\n%s' - % (stderr, stdout)) + raise MathExtError('dvipng exited with error:\n[stderr]\n%s\n' + '[stdout]\n%s' % (stderr, stdout)) depth = None if use_preview: for line in stdout.splitlines(): diff --git a/sphinx/ext/refcounting.py b/sphinx/ext/refcounting.py index c31d6627..cad9d7f1 100644 --- a/sphinx/ext/refcounting.py +++ b/sphinx/ext/refcounting.py @@ -55,7 +55,8 @@ class Refcounts(dict): refcount = None else: refcount = int(refcount) - # Update the entry with the new parameter or the result information. + # Update the entry with the new parameter or the result + # information. if arg: entry.args.append((arg, type, refcount)) else: @@ -81,7 +82,8 @@ class Refcounts(dict): if entry.result_refs is None: rc += "Always NULL." else: - rc += (entry.result_refs and "New" or "Borrowed") + " reference." + rc += (entry.result_refs and "New" or "Borrowed") + \ + " reference." node.insert(0, refcount(rc, rc)) diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index c8c6412c..61dd4d67 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -14,42 +14,63 @@ from docutils import nodes -from sphinx.util.compat import make_admonition +from sphinx.util.compat import Directive, make_admonition class todo_node(nodes.Admonition, nodes.Element): pass class todolist(nodes.General, nodes.Element): pass -def todo_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env +class Todo(Directive): + """ + A todo entry, displayed (if configured) in the form of an admonition. + """ - targetid = "todo-%s" % env.index_num - env.index_num += 1 - targetnode = nodes.target('', '', ids=[targetid]) + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} - ad = make_admonition(todo_node, name, [_('Todo')], options, content, lineno, - content_offset, block_text, state, state_machine) + def run(self): + env = self.state.document.settings.env + + targetid = "todo-%s" % env.index_num + env.index_num += 1 + targetnode = nodes.target('', '', ids=[targetid]) + + ad = make_admonition(todo_node, self.name, [_('Todo')], self.options, + self.content, self.lineno, self.content_offset, + self.block_text, self.state, self.state_machine) + + # Attach a list of all todos to the environment, + # the todolist works with the collected todo nodes + if not hasattr(env, 'todo_all_todos'): + env.todo_all_todos = [] + env.todo_all_todos.append({ + 'docname': env.docname, + 'lineno': self.lineno, + 'todo': ad[0].deepcopy(), + 'target': targetnode, + }) + + return [targetnode] + ad - # Attach a list of all todos to the environment, - # the todolist works with the collected todo nodes - if not hasattr(env, 'todo_all_todos'): - env.todo_all_todos = [] - env.todo_all_todos.append({ - 'docname': env.docname, - 'lineno': lineno, - 'todo': ad[0].deepcopy(), - 'target': targetnode, - }) - return [targetnode] + ad +class TodoList(Directive): + """ + A list of all todo entries. + """ + has_content = False + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} -def todolist_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # Simply insert an empty todolist node which will be replaced later - # when process_todo_nodes is called - return [todolist('')] + def run(self): + # Simply insert an empty todolist node which will be replaced later + # when process_todo_nodes is called + return [todolist('')] def process_todo_nodes(app, doctree, fromdocname): @@ -75,8 +96,8 @@ def process_todo_nodes(app, doctree, fromdocname): para = nodes.paragraph() filename = env.doc2path(todo_info['docname'], base=None) description = ( - _('(The original entry is located in %s, line %d and can be found ') % - (filename, todo_info['lineno'])) + _('(The original entry is located in %s, line %d and ' + 'can be found ') % (filename, todo_info['lineno'])) para += nodes.Text(description, description) # Create a reference @@ -119,8 +140,8 @@ def setup(app): latex=(visit_todo_node, depart_todo_node), text=(visit_todo_node, depart_todo_node)) - app.add_directive('todo', todo_directive, 1, (0, 0, 1)) - app.add_directive('todolist', todolist_directive, 0, (0, 0, 0)) + app.add_directive('todo', Todo) + app.add_directive('todolist', TodoList) app.connect('doctree-resolved', process_todo_nodes) app.connect('env-purge-doc', purge_todos) diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index fe82b0bc..61c413d7 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -30,6 +30,8 @@ try: from pygments.token import Generic, Comment, Number except ImportError: pygments = None + lexers = None + HtmlFormatter = LatexFormatter = None else: class SphinxStyle(Style): """ @@ -47,12 +49,15 @@ else: Number: '#208050', }) + class NoneStyle(Style): + """Style without any styling.""" + lexers = dict( none = TextLexer(), python = PythonLexer(), pycon = PythonConsoleLexer(), - # the python3 option exists as of Pygments 0.12, but it doesn't - # do any harm in previous versions + # the python3 option exists as of Pygments 1.0, + # but it doesn't do any harm in previous versions pycon3 = PythonConsoleLexer(python3=True), rest = RstLexer(), c = CLexer(), @@ -81,22 +86,33 @@ if sys.version_info < (2, 5): class PygmentsBridge(object): + # Set these attributes if you want to have different Pygments formatters + # than the default ones. + html_formatter = HtmlFormatter + latex_formatter = LatexFormatter + def __init__(self, dest='html', stylename='sphinx'): self.dest = dest if not pygments: return - if stylename == 'sphinx': + if stylename is None or stylename == 'sphinx': style = SphinxStyle + elif stylename == 'none': + style = NoneStyle elif '.' in stylename: module, stylename = stylename.rsplit('.', 1) - style = getattr(__import__(module, None, None, ['']), stylename) + style = getattr(__import__(module, None, None, ['__name__']), + stylename) else: style = get_style_by_name(stylename) - self.hfmter = {False: HtmlFormatter(style=style), - True: HtmlFormatter(style=style, linenos=True)} - self.lfmter = {False: LatexFormatter(style=style, commandprefix='PYG'), - True: LatexFormatter(style=style, linenos=True, - commandprefix='PYG')} + if dest == 'html': + self.fmter = {False: self.html_formatter(style=style), + True: self.html_formatter(style=style, linenos=True)} + else: + self.fmter = {False: self.latex_formatter(style=style, + commandprefix='PYG'), + True: self.latex_formatter(style=style, linenos=True, + commandprefix='PYG')} def unhighlighted(self, source): if self.dest == 'html': @@ -170,9 +186,9 @@ class PygmentsBridge(object): lexer.add_filter('raiseonerror') try: if self.dest == 'html': - return highlight(source, lexer, self.hfmter[bool(linenos)]) + return highlight(source, lexer, self.fmter[bool(linenos)]) else: - hlsource = highlight(source, lexer, self.lfmter[bool(linenos)]) + hlsource = highlight(source, lexer, self.fmter[bool(linenos)]) return hlsource.translate(tex_hl_escape_map) except ErrorToken: # this is most probably not the selected language, @@ -186,9 +202,9 @@ class PygmentsBridge(object): # no HTML styles needed return '' if self.dest == 'html': - return self.hfmter[0].get_style_defs() + return self.fmter[0].get_style_defs() else: - styledefs = self.lfmter[0].get_style_defs() + styledefs = self.fmter[0].get_style_defs() # workaround for Pygments < 0.12 if styledefs.startswith('\\newcommand\\at{@}'): styledefs += _LATEX_STYLES diff --git a/sphinx/htmlhelp.py b/sphinx/htmlhelp.py deleted file mode 100644 index 84ef31d9..00000000 --- a/sphinx/htmlhelp.py +++ /dev/null @@ -1,220 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.htmlhelp - ~~~~~~~~~~~~~~~ - - Build HTML help support files. - Adapted from the original Doc/tools/prechm.py. - - :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import os -import cgi -from os import path - -from docutils import nodes - -from sphinx import addnodes - -# Project file (*.hhp) template. 'outname' is the file basename (like -# the pythlp in pythlp.hhp); 'version' is the doc version number (like -# the 2.2 in Python 2.2). -# The magical numbers in the long line under [WINDOWS] set most of the -# user-visible features (visible buttons, tabs, etc). -# About 0x10384e: This defines the buttons in the help viewer. The -# following defns are taken from htmlhelp.h. Not all possibilities -# actually work, and not all those that work are available from the Help -# Workshop GUI. In particular, the Zoom/Font button works and is not -# available from the GUI. The ones we're using are marked with 'x': -# -# 0x000002 Hide/Show x -# 0x000004 Back x -# 0x000008 Forward x -# 0x000010 Stop -# 0x000020 Refresh -# 0x000040 Home x -# 0x000080 Forward -# 0x000100 Back -# 0x000200 Notes -# 0x000400 Contents -# 0x000800 Locate x -# 0x001000 Options x -# 0x002000 Print x -# 0x004000 Index -# 0x008000 Search -# 0x010000 History -# 0x020000 Favorites -# 0x040000 Jump 1 -# 0x080000 Jump 2 -# 0x100000 Zoom/Font x -# 0x200000 TOC Next -# 0x400000 TOC Prev - -project_template = '''\ -[OPTIONS] -Binary TOC=Yes -Binary Index=No -Compiled file=%(outname)s.chm -Contents file=%(outname)s.hhc -Default Window=%(outname)s -Default topic=index.html -Display compile progress=No -Full text search stop list file=%(outname)s.stp -Full-text search=Yes -Index file=%(outname)s.hhk -Language=0x409 -Title=%(title)s - -[WINDOWS] -%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\ -"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 - -[FILES] -''' - -contents_header = '''\ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<HTML> -<HEAD> -<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"> -<!-- Sitemap 1.0 --> -</HEAD><BODY> -<OBJECT type="text/site properties"> - <param name="Window Styles" value="0x801227"> - <param name="ImageType" value="Folder"> -</OBJECT> -<UL> -''' - -contents_footer = '''\ -</UL></BODY></HTML> -''' - -object_sitemap = '''\ -<OBJECT type="text/sitemap"> - <param name="Name" value="%s"> - <param name="Local" value="%s"> -</OBJECT> -''' - -# List of words the full text search facility shouldn't index. This -# becomes file outname.stp. Note that this list must be pretty small! -# Different versions of the MS docs claim the file has a maximum size of -# 256 or 512 bytes (including \r\n at the end of each line). -# Note that "and", "or", "not" and "near" are operators in the search -# language, so no point indexing them even if we wanted to. -stopwords = """ -a and are as at -be but by -for -if in into is it -near no not -of on or -such -that the their then there these they this to -was will with -""".split() - - -def build_hhx(builder, outdir, outname): - builder.info('dumping stopword list...') - f = open(path.join(outdir, outname+'.stp'), 'w') - try: - for word in sorted(stopwords): - print >>f, word - finally: - f.close() - - builder.info('writing project file...') - f = open(path.join(outdir, outname+'.hhp'), 'w') - try: - f.write(project_template % {'outname': outname, - 'title': builder.config.html_title, - 'version': builder.config.version, - 'project': builder.config.project}) - if not outdir.endswith(os.sep): - outdir += os.sep - olen = len(outdir) - for root, dirs, files in os.walk(outdir): - staticdir = (root == path.join(outdir, '_static')) - for fn in files: - if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'): - print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\') - finally: - f.close() - - builder.info('writing TOC file...') - f = open(path.join(outdir, outname+'.hhc'), 'w') - try: - f.write(contents_header) - # special books - f.write('<LI> ' + object_sitemap % (builder.config.html_short_title, - 'index.html')) - if builder.config.html_use_modindex: - f.write('<LI> ' + object_sitemap % (_('Global Module Index'), - 'modindex.html')) - # the TOC - tocdoc = builder.env.get_and_resolve_doctree(builder.config.master_doc, builder, - prune_toctrees=False) - def write_toc(node, ullevel=0): - if isinstance(node, nodes.list_item): - f.write('<LI> ') - for subnode in node: - write_toc(subnode, ullevel) - elif isinstance(node, nodes.reference): - link = node['refuri'] - title = cgi.escape(node.astext()).replace('"','"') - item = object_sitemap % (title, link) - f.write(item.encode('ascii', 'xmlcharrefreplace')) - elif isinstance(node, nodes.bullet_list): - if ullevel != 0: - f.write('<UL>\n') - for subnode in node: - write_toc(subnode, ullevel+1) - if ullevel != 0: - f.write('</UL>\n') - elif isinstance(node, addnodes.compact_paragraph): - for subnode in node: - write_toc(subnode, ullevel) - istoctree = lambda node: isinstance(node, addnodes.compact_paragraph) and \ - node.has_key('toctree') - for node in tocdoc.traverse(istoctree): - write_toc(node) - f.write(contents_footer) - finally: - f.close() - - builder.info('writing index file...') - index = builder.env.create_index(builder) - f = open(path.join(outdir, outname+'.hhk'), 'w') - try: - f.write('<UL>\n') - def write_index(title, refs, subitems): - def write_param(name, value): - item = ' <param name="%s" value="%s">\n' % (name, value) - f.write(item.encode('ascii', 'xmlcharrefreplace')) - title = cgi.escape(title) - f.write('<LI> <OBJECT type="text/sitemap">\n') - write_param('Keyword', title) - if len(refs) == 0: - write_param('See Also', title) - elif len(refs) == 1: - write_param('Local', refs[0]) - else: - for i, ref in enumerate(refs): - write_param('Name', '[%d] %s' % (i, ref)) # XXX: better title? - write_param('Local', ref) - f.write('</OBJECT>\n') - if subitems: - f.write('<UL> ') - for subitem in subitems: - write_index(subitem[0], subitem[1], []) - f.write('</UL>') - for (key, group) in index: - for title, (refs, subitems) in group: - write_index(title, refs, subitems) - f.write('</UL>\n') - finally: - f.close() diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py new file mode 100644 index 00000000..679447a8 --- /dev/null +++ b/sphinx/jinja2glue.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" + sphinx.jinja2glue + ~~~~~~~~~~~~~~~~~ + + Glue code for the jinja2 templating engine. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs +from os import path +from pprint import pformat + +from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, \ + contextfunction +from jinja2.sandbox import SandboxedEnvironment + +from sphinx.util import mtimes_of_files +from sphinx.application import TemplateBridge + + +def _tobool(val): + if isinstance(val, basestring): + return val.lower() in ('true', '1', 'yes', 'on') + return bool(val) + +def accesskey(context, key): + """Helper to output each access key only once.""" + if '_accesskeys' not in context: + context.vars['_accesskeys'] = {} + if key not in context.vars['_accesskeys']: + context.vars['_accesskeys'][key] = 1 + return 'accesskey="%s"' % key + return '' + + +class BuiltinTemplateLoader(TemplateBridge, BaseLoader): + """ + Interfaces the rendering environment of jinja2 for use in Sphinx. + """ + + # TemplateBridge interface + + def init(self, builder, theme=None, dirs=None): + # create a chain of paths to search + if theme: + # the theme's own dir and its bases' dirs + chain = theme.get_dirchain() + # then the theme parent paths + chain.extend(theme.themepath) + elif dirs: + chain = list(dirs) + else: + chain = [] + + # prepend explicit template paths + self.templatepathlen = len(builder.config.templates_path) + if builder.config.templates_path: + chain[0:0] = [path.join(builder.confdir, tp) + for tp in builder.config.templates_path] + + # store it for use in newest_template_mtime + self.pathchain = chain + + # make the paths into loaders + self.loaders = map(FileSystemLoader, chain) + + use_i18n = builder.translator is not None + extensions = use_i18n and ['jinja2.ext.i18n'] or [] + self.environment = SandboxedEnvironment(loader=self, + extensions=extensions) + self.environment.filters['tobool'] = _tobool + self.environment.globals['debug'] = contextfunction(pformat) + self.environment.globals['accesskey'] = contextfunction(accesskey) + if use_i18n: + self.environment.install_gettext_translations(builder.translator) + + def render(self, template, context): + return self.environment.get_template(template).render(context) + + def render_string(self, source, context): + return self.environment.from_string(source).render(context) + + def newest_template_mtime(self): + return max(mtimes_of_files(self.pathchain, '.html')) + + # Loader interface + + def get_source(self, environment, template): + loaders = self.loaders + # exclamation mark starts search from theme + if template.startswith('!'): + loaders = loaders[self.templatepathlen:] + template = template[1:] + for loader in loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.js b/sphinx/locale/cs/LC_MESSAGES/sphinx.js index 4b814e4f..42fa9abd 100644 --- a/sphinx/locale/cs/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "cs", "plural_expr": "(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)", "messages": {"module, in ": "modul", "Preparing search...": "", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "", "Search finished, found %s page(s) matching the search query.": "", ", in ": "", "Permalink to this headline": "Trval\u00fd odkaz na tento nadpis", "Searching": "hledej", "Permalink to this definition": "Trval\u00fd odkaz na tuto definici", "Hide Search Matches": "", "Search Results": "V\u00fdsledky hled\u00e1n\u00ed"}});
\ No newline at end of file +Documentation.addTranslations({"locale": "cs", "plural_expr": "(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)", "messages": {"module, in ": "modul, v", "Preparing search...": "P\u0159ipravuji vyhled\u00e1v\u00e1n\u00ed....", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Nenalezli jsme \u017e\u00e1dn\u00fd dokument. Ujist\u011bte se pros\u00edm, \u017ee v\u0161echna slova jsou spr\u00e1vn\u011b a \u017ee jste vybral dostatek kategori\u00ed.", "Search finished, found %s page(s) matching the search query.": "Vyhled\u00e1v\u00e1n\u00ed skon\u010dilo, nalezeno %s stran.", ", in ": ", v", "Permalink to this headline": "Trval\u00fd odkaz na tento nadpis", "Searching": "Hled\u00e1m", "Permalink to this definition": "Trval\u00fd odkaz na tuto definici", "Hide Search Matches": "Skr\u00fdt v\u00fdsledky vyhled\u00e1v\u00e1n\u00ed", "Search Results": "V\u00fdsledky hled\u00e1n\u00ed"}});
\ No newline at end of file diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.mo b/sphinx/locale/cs/LC_MESSAGES/sphinx.mo Binary files differindex c50de3db..3dccc2b9 100644 --- a/sphinx/locale/cs/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.po b/sphinx/locale/cs/LC_MESSAGES/sphinx.po index b6936022..7d727399 100644 --- a/sphinx/locale/cs/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2008-08-10 11:43+0000\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"POT-Creation-Date: 2008-11-27 18:39+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Pavel Kosina <pavel.kosina@gmail.com>\n" "Language-Team: Pavel Kosina <pavel.kosina@gmail.com>\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -16,88 +16,30 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d.%m.%Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Rejstřík indexů" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "index" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Rejstřík modulů" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "moduly" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "další" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "předchozí" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Vestavěné funkce" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Úroveň modulů" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d.%m.%Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Index" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 -#, fuzzy +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" -msgstr "Rejstřík modulů" +msgstr "Rejstřík modulů " -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 -#, fuzzy +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Vyhledávací stránka" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Trvalý odkaz na tuto definici" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Trvalý odkaz na tento nadpis" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Vydání" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "promměná prostředí, %s" @@ -107,14 +49,48 @@ msgstr "promměná prostředí, %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Vestavěné funkce " + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Úroveň modulů" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Platforma: %s" +msgid "%b %d, %Y" +msgstr "%d.%m.%Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[obrázek]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Rejstřík indexů" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "index" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Celkový rejstřík modulů" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "moduly" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "další" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "předchozí" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "(v" #: sphinx/directives/desc.py:25 #, python-format @@ -132,15 +108,15 @@ msgstr "%s() (v modulu %s)" msgid "%s (built-in variable)" msgstr "%s() (vestavěná proměnná)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s() (v modulu %s)" #: sphinx/directives/desc.py:33 -#, fuzzy, python-format +#, python-format msgid "%s (built-in class)" -msgstr "%s() (vestavěná proměnná)" +msgstr "%s () (vestavěná proměnná)" #: sphinx/directives/desc.py:34 #, python-format @@ -167,65 +143,70 @@ msgstr "%s() (statická metoda %s.%s)" msgid "%s() (%s static method)" msgstr "%s() (statická metoda %s)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s() (atribut %s.%s)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s() (atribut %s)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C funkce)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (člen C)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C makro)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C typ)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C proměnná)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Vyvolá" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Proměnná" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Vrací" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Typ navrácené hodnoty" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parametry" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parametry" -#: sphinx/directives/desc.py:423 -#, fuzzy, python-format +#: sphinx/directives/desc.py:465 +#, python-format msgid "%scommand line option; %s" -msgstr "%sparametry příkazového řádku; %s" +msgstr "%s parametry příkazového řádku; %s" #: sphinx/directives/other.py:101 msgid "Platforms: " @@ -248,22 +229,32 @@ msgstr "Autor modulu: " msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Viz také" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" -msgstr "" +msgstr "Todo" #: sphinx/ext/todo.py:75 #, python-format msgid "(The original entry is located in %s, line %d and can be found " -msgstr "" +msgstr "(Původní záznam je v %s, řádka %d a lze jej nalézt" #: sphinx/ext/todo.py:81 msgid "here" -msgstr "" +msgstr "zde" #: sphinx/locale/__init__.py:15 msgid "Attention" @@ -348,42 +339,50 @@ msgstr "příkaz" msgid "built-in function" msgstr "vestavěná funkce" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Trvalý odkaz na tento nadpis" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Trvalý odkaz na tuto definici" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" -msgstr "" +msgstr "Skrýt výsledky vyhledávání" #: sphinx/static/searchtools.js:274 -#, fuzzy msgid "Searching" -msgstr "hledej" +msgstr "Hledám" #: sphinx/static/searchtools.js:279 msgid "Preparing search..." -msgstr "" +msgstr "Připravuji vyhledávání...." #: sphinx/static/searchtools.js:338 -#, fuzzy msgid "module, in " -msgstr "modul" +msgstr "modul, v" #: sphinx/static/searchtools.js:347 msgid ", in " -msgstr "" +msgstr ", v" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Výsledky hledání" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "" +"Nenalezli jsme žádný dokument. Ujistěte se prosím, že všechna slova jsou " +"správně a že jste vybral dostatek kategorií." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." -msgstr "" +msgstr "Vyhledávání skončilo, nalezeno %s stran." #: sphinx/templates/defindex.html:2 msgid "Overview" @@ -444,27 +443,23 @@ msgstr "Obsah" msgid "Previous topic" msgstr "Přechozí téma" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "předchozí kapitola" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Další téma" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "další kapitola" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Tato stránka" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Návrh změnu" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Ukázat zdroj" @@ -472,52 +467,49 @@ msgstr "Ukázat zdroj" msgid "Quick search" msgstr "Rychlé vyhledávání" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Hledání dle klíče" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "hledej" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Zadej jméno modulu, třídy nebo funkce." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Hledání uvnitř %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "O těchto dokumentech" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Hledání" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Veškerá práva vyhrazena" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." -msgstr "Naposledy aktualizováno dne %(last_updated)s." +msgstr "Aktualizováno dne %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -526,15 +518,7 @@ msgstr "" "Vytvořeno pomocí <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Nejpopulárnější moduly:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Zobrazit moduly dostupné na této platformě" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Zastaralé" @@ -543,34 +527,29 @@ msgstr "Zastaralé" msgid "Search %(docstitle)s" msgstr "Prohledat %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Poznámka:</strong> Stránka, kterou hledáte, " -"neexistuje.<br>Snažili jsme se najít nové umístění této stránky, ale " -"nepovedlo se." -#: sphinx/templates/search.html:7 -#, fuzzy +#: sphinx/templates/search.html:14 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" " function will automatically search for all of the words. Pages\n" " containing fewer words won't appear in the result list." msgstr "" -"Toto je vyhledávací stránka. Zadejte klíčová slova do pole níže a " -"klikněte na \"hledej\". \n" -"Prohledávání funkcí hledá automaticky všechna slova. Stránky obsahující" -" slov méně, nebudou nalezeny." +"Toto je vyhledávací stránka. Zadejte klíčová slova a klikněte na " +"\"hledej\". \n" +"Vyhledávání hledá automaticky všechna slova. Nebudou tedy nalezeny " +"stránky, obsahující méně slov." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "hledej" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Nic jsme nenašli." @@ -602,3 +581,40 @@ msgstr "Změny API" msgid "Other changes" msgstr "Ostatní změny" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Vydání" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Platforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[obrázek]" + +#~ msgid "Suggest Change" +#~ msgstr "Návrh změnu" + +#~ msgid "Keyword search" +#~ msgstr "Hledání dle klíče" + +#~ msgid "Most popular modules:" +#~ msgstr "Nejpopulárnější moduly:" + +#~ msgid "Show modules only available on these platforms" +#~ msgstr "Zobrazit moduly dostupné na této platformě" + +#~ msgid "" +#~ "<strong>Note:</strong> You requested an " +#~ "out-of-date URL from this server. " +#~ "We've tried to redirect you to the" +#~ " new location of this page, but " +#~ "it may not be the right one." +#~ msgstr "" +#~ "<strong>Poznámka:</strong> Stránka, kterou hledáte," +#~ " neexistuje.<br>Snažili jsme se najít nové" +#~ " umístění této stránky, ale nepovedlo " +#~ "se." + diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.js b/sphinx/locale/de/LC_MESSAGES/sphinx.js index 98aa5c12..542c094f 100644 --- a/sphinx/locale/de/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/de/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "de", "plural_expr": "(n != 1)", "messages": {"module, in ": "Modul, in ", "Preparing search...": "Suche wird vorbereitet...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Es wurden keine Dokumente gefunden. Haben Sie alle Suchworte richtig geschrieben und gen\u00fcgend Kategorien ausgew\u00e4hlt?", "Search finished, found %s page(s) matching the search query.": "Suche beendet, %s Seite(n) mit Ergebnissen wurden gefunden.", ", in ": "", "Permalink to this headline": "Permalink zu dieser \u00dcberschrift", "Searching": "Suchen...", "Permalink to this definition": "Permalink zu dieser Definition", "Hide Search Matches": "Suchergebnisse ausblenden", "Search Results": "Suchergebnisse"}});
\ No newline at end of file +Documentation.addTranslations({"locale": "de", "plural_expr": "(n != 1)", "messages": {"module, in ": "Modul, in ", "Preparing search...": "Suche wird vorbereitet...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Es wurden keine Dokumente gefunden. Haben Sie alle Suchworte richtig geschrieben und gen\u00fcgend Kategorien ausgew\u00e4hlt?", "Search finished, found %s page(s) matching the search query.": "Suche beendet, %s Seite(n) mit Ergebnissen wurden gefunden.", ", in ": ", in ", "Permalink to this headline": "Permalink zu dieser \u00dcberschrift", "Searching": "Suchen...", "Permalink to this definition": "Permalink zu dieser Definition", "Hide Search Matches": "Suchergebnisse ausblenden", "Search Results": "Suchergebnisse"}});
\ No newline at end of file diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.mo b/sphinx/locale/de/LC_MESSAGES/sphinx.mo Binary files differindex 462f7c7f..1f8f8d2d 100644 --- a/sphinx/locale/de/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/de/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.po b/sphinx/locale/de/LC_MESSAGES/sphinx.po index 5fdd4a96..a830aeef 100644 --- a/sphinx/locale/de/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/de/LC_MESSAGES/sphinx.po @@ -7,93 +7,37 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-08-07 21:40+0200\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:43+0000\n" "Last-Translator: Horst Gutmann <zerok@zerokspot.com>\n" "Language-Team: de <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d. %m. %Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Allgemeiner Index" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "Index" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Globaler Modulindex" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "Module" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "weiter" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "zurück" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Builtins" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Modulebene" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d. %m. %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Stichwortverzeichnis" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Modulindex" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Suche" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Permalink zu dieser Definition" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Permalink zu dieser Überschrift" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Release" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "Umgebungsvariable; %s" @@ -103,14 +47,48 @@ msgstr "Umgebungsvariable; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Builtins" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Modulebene" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Plattform: %s" +msgid "%b %d, %Y" +msgstr "%d. %m. %Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[Bild]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Allgemeiner Index" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "Index" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Globaler Modulindex" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "Module" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "weiter" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "zurück" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr " (in " #: sphinx/directives/desc.py:25 #, python-format @@ -128,7 +106,7 @@ msgstr "%s() (in Modul %s)" msgid "%s (built-in variable)" msgstr "%s (eingebaute Variable)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (in Modul %s)" @@ -163,62 +141,66 @@ msgstr "%s() (statische Methode von %s.%s)" msgid "%s() (%s static method)" msgstr "%s() (statische Methode von %s)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (Attribut von %s.%s)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (Attribut von %s)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C-Funktion)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (C-Member)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C-Makro)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C-Typ)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C-Variable)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Verursacht:" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Variable" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Rückgabe" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Rückgabetyp" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +msgid "Parameter" +msgstr "Parameter" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parameter" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "%sKommandozeilenoption; %s" @@ -244,22 +226,32 @@ msgstr "Autor des Moduls: " msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Siehe auch" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr " Basisklassen: %s" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "Alias von :class:`%s`" + #: sphinx/ext/todo.py:31 msgid "Todo" -msgstr "" +msgstr "Zu tun" #: sphinx/ext/todo.py:75 #, python-format msgid "(The original entry is located in %s, line %d and can be found " -msgstr "" +msgstr "(Der Eintrag steht in %s, Zeile %s, siehe " #: sphinx/ext/todo.py:81 msgid "here" -msgstr "" +msgstr "hier" #: sphinx/locale/__init__.py:15 msgid "Attention" @@ -344,6 +336,14 @@ msgstr "Statement" msgid "built-in function" msgstr "eingebaute Funktion" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Permalink zu dieser Überschrift" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Permalink zu dieser Definition" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Suchergebnisse ausblenden" @@ -362,13 +362,13 @@ msgstr "Modul, in " #: sphinx/static/searchtools.js:347 msgid ", in " -msgstr "" +msgstr ", in " -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Suchergebnisse" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -376,7 +376,7 @@ msgstr "" "Es wurden keine Dokumente gefunden. Haben Sie alle Suchworte richtig " "geschrieben und genügend Kategorien ausgewählt?" -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Suche beendet, %s Seite(n) mit Ergebnissen wurden gefunden." @@ -440,27 +440,23 @@ msgstr "Inhalt" msgid "Previous topic" msgstr "Vorheriges Thema" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "vorheriges Kapitel" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Nächstes Thema" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "nächstes Kapitel" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Diese Seite" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Änderung vorschlagen" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Quelltext anzeigen" @@ -468,52 +464,48 @@ msgstr "Quelltext anzeigen" msgid "Quick search" msgstr "Schnellsuche" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Stichwortsuche" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Los" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." -msgstr "Gib einen Modul-, Klassen- oder Funktionsnamen an." +msgid "Enter search terms or a module, class or function name." +msgstr "Geben Sie einen Modul-, Klassen- oder Funktionsnamen einn." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Suche in %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "Über diese Dokumentation" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Suche" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Zuletzt aktualisiert am %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -522,15 +514,7 @@ msgstr "" "Mit <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s " "erstellt." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Beliebteste Module:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Zeige nur Module, die auf diesen Plattformen verfügbar sind" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Veraltet" @@ -539,35 +523,25 @@ msgstr "Veraltet" msgid "Search %(docstitle)s" msgstr "Suche in %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." -msgstr "" -"<strong>Anmerkung:</strong> Du hast eine nicht länger gültige URL von " -"diesem Server angefragt. Wir haben versucht dich auf die neue Adresse " -"dieser Seite umzuleiten, aber dies muss nicht die richtige Seite sein." +"Please activate JavaScript to enable the search\n" +" functionality." +msgstr "Bitte aktivieren Sie JavaScript, wenn Sie die Suchfunktion nutzen wollen." -#: sphinx/templates/search.html:7 -#, fuzzy +#: sphinx/templates/search.html:14 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" " function will automatically search for all of the words. Pages\n" " containing fewer words won't appear in the result list." -msgstr "" -"Von hier aus kannst du die Dokumentation durchsuchen. Gib deine " -"Suchbegriffe in das untenstehende Feld ein und klicke auf \"suchen\". " -"Bitte beachte, dass die Suchfunktion automatisch nach all diesen Worten " -"suchen wird. Seiten, die nicht alle Worte enthalten, werden nicht in der " -"Ergebnisliste erscheinen." +msgstr "Von hier aus können Sie die Dokumentation durchsuchen. Geben Sie Ihre Suchbegriffe in das untenstehende Feld ein und klicken Sie auf \"Suchen\". Bitte beachten Sie, dass die Suchfunktion automatisch nach all diesen Worten suchen wird. Seiten, die nicht alle Worte enthalten, werden nicht in der Ergebnisliste erscheinen." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "suchen" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Deine Suche ergab leider keine Treffer." @@ -599,3 +573,16 @@ msgstr "C API-Änderungen" msgid "Other changes" msgstr "Andere Änderungen" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Release" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Plattform: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[Bild]" + diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.mo b/sphinx/locale/es/LC_MESSAGES/sphinx.mo Binary files differindex d1ddb079..2989194c 100644 --- a/sphinx/locale/es/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/es/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.po b/sphinx/locale/es/LC_MESSAGES/sphinx.po index 288e0090..09ebda8b 100644 --- a/sphinx/locale/es/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/es/LC_MESSAGES/sphinx.po @@ -8,96 +8,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: guillem@torroja.dmt.upm.es\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Guillem Borrell <guillem@torroja.dmt.upm.es>\n" "Language-Team: es <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d %b, %Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Índice General" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "índice" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Índice Global de Módulos" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "módulos" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "siguiente" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "anterior" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -#, fuzzy -msgid "Builtins" -msgstr "Funciones de base" - -#: sphinx/builder.py:1131 -#, fuzzy -msgid "Module level" -msgstr "Módulos" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, fuzzy, python-format msgid "%B %d, %Y" msgstr "%d de %B de %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Índice" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Índice de Módulos" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Página de Búsqueda" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Enlazar permanentemente con esta definición" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Enlazar permanentemente con este título" - -#: sphinx/latexwriter.py:172 -#, fuzzy -msgid "Release" -msgstr "Versión" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "variables de entorno; %s" @@ -107,14 +48,50 @@ msgstr "variables de entorno; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +#, fuzzy +msgid "Builtins" +msgstr "Funciones de base" + +#: sphinx/builders/changes.py:66 +#, fuzzy +msgid "Module level" +msgstr "Módulos" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Plataforma: %s" +msgid "%b %d, %Y" +msgstr "%d %b, %Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[imagen]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Índice General" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "índice" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Índice Global de Módulos" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "módulos" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "siguiente" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "anterior" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" #: sphinx/directives/desc.py:25 #, fuzzy, python-format @@ -132,7 +109,7 @@ msgstr "%s() (en el módulo %s)" msgid "%s (built-in variable)" msgstr "%s (variable de base)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (en el módulo %s)" @@ -167,63 +144,68 @@ msgstr "%s() (%s.%s método estático)" msgid "%s() (%s static method)" msgstr "%s() (%s método estático)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atributo)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atributo)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (función C)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (miembro C)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (macro C)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (tipo C)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (variable C)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Muestra" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Variable" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Devuelve" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 #, fuzzy msgid "Return type" msgstr "Tipo del argumento devuelto" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parámetros" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parámetros" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, fuzzy, python-format msgid "%scommand line option; %s" msgstr "%sOpciones en línea de comandos; %s" @@ -249,10 +231,20 @@ msgstr "Autor del módulo" msgid "Author: " msgstr "Autor:" -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Ver también" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "" @@ -350,6 +342,14 @@ msgstr "sentencia" msgid "built-in function" msgstr "función de base" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Enlazar permanentemente con este título" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Enlazar permanentemente con esta definición" + #: sphinx/static/doctools.js:174 #, fuzzy msgid "Hide Search Matches" @@ -372,11 +372,11 @@ msgstr "módulo" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Resultados de la búsqueda" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -385,7 +385,7 @@ msgstr "" "todas las palabras correctamente y que ha seleccionado suficientes " "categorías" -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -451,27 +451,23 @@ msgstr "Contenidos" msgid "Previous topic" msgstr "Tema anterior" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "Capítulo anterior" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Próximo tema" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "Próximo capítulo" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Esta página" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Sugerir una modificación" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Enseñar el código" @@ -479,52 +475,49 @@ msgstr "Enseñar el código" msgid "Quick search" msgstr "Búsqueda rápida" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Búsqueda por palabras clave" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Ir a" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Introducir en nombre de un módulo, clase o función" -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Buscar en %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "Sobre este documento" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Búsqueda" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\\\"%(path)s\\\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Actualizado por última vez en %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -533,15 +526,7 @@ msgstr "" "Creado con <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Módulos más comunes:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Mostrar sólo los módulos disponibles en estas plataformas" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Obsoleto" @@ -550,17 +535,13 @@ msgstr "Obsoleto" msgid "Search %(docstitle)s" msgstr "Buscar en %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Nota:</strong> Has solicitado una dirección desactualizada a este" -" servidor. Hemos intentado redirigirte a la nueva dirección de la misma " -"página pero puede no ser la correcta." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -573,11 +554,11 @@ msgstr "" " las palabras. Las páginas que contengan menos palabras no aparecerán en" " la lista de resultados." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "buscar" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Tu consulta no obtuvo ningún resultado" @@ -609,3 +590,17 @@ msgstr "Cambios en la API C" msgid "Other changes" msgstr "Otros cambios" +#: sphinx/writers/latex.py:173 +#, fuzzy +msgid "Release" +msgstr "Versión" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Plataforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[imagen]" + diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.js b/sphinx/locale/fi/LC_MESSAGES/sphinx.js new file mode 100644 index 00000000..f654e7e1 --- /dev/null +++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "fi", "plural_expr": "(n != 1)", "messages": {"module, in ": "", "Preparing search...": "Valmistellaan etsint\u00e4\u00e4...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Ei l\u00f6ytynyt yht\u00e4\u00e4n. Tarkista hakuehdot, sanahaku, ei sen osia", "Search finished, found %s page(s) matching the search query.": "Etsint\u00e4 tehty, l\u00f6ydetty %s sivu(a).", ", in ": "", "Permalink to this headline": "", "Searching": "Etsit\u00e4\u00e4n", "Permalink to this definition": "", "Hide Search Matches": "Piilota l\u00f6ydetyt", "Search Results": "Etsinn\u00e4n tulos"}});
\ No newline at end of file diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.mo b/sphinx/locale/fi/LC_MESSAGES/sphinx.mo Binary files differnew file mode 100644 index 00000000..3207b738 --- /dev/null +++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.po b/sphinx/locale/fi/LC_MESSAGES/sphinx.po new file mode 100644 index 00000000..7bbe636e --- /dev/null +++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.po @@ -0,0 +1,600 @@ +# Finnish translations for Sphinx. +# Copyright (C) 2009 ORGANIZATION +# This file is distributed under the same license as the Sphinx project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: Sphinx 0.6\n" +"Report-Msgid-Bugs-To: sphinx@awot.fi\n" +"POT-Creation-Date: 2009-01-24 18:39+0000\n" +"PO-Revision-Date: 2009-02-11 11:21+0200\n" +"Last-Translator: Jukka Inkeri <sphinx@awot.fi>\n" +"Language-Team: fi <sphinx@awot.fi>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.4\n" + +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 +#, python-format +msgid "%B %d, %Y" +msgstr "%d.%m.%Y" + +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 +#: sphinx/templates/genindex-split.html:2 +#: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 +#: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 +msgid "Index" +msgstr "Sisällysluettelo" + +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 +msgid "Module Index" +msgstr "Moduuli sisällysluettelo" + +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 +msgid "Search Page" +msgstr "Etsi sivu" + +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 +#, python-format +msgid "environment variable; %s" +msgstr "" + +#: sphinx/roles.py:60 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" +msgstr "" + +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Moduulitaso" + + +#: sphinx/builders/html.py:118 +#, python-format +msgid "%b %d, %Y" +msgstr "%d.%m.%Y" + +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Yleinen sisällysluettelo" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "hakemisto" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Yleinen moduulien sisällysluettelo" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "moduulit" +#msgstr "osat" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr ">" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "<" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" + +#: sphinx/directives/desc.py:25 +#, python-format +msgid "%s() (built-in function)" +msgstr "" + +#: sphinx/directives/desc.py:26 sphinx/directives/desc.py:42 +#: sphinx/directives/desc.py:54 +#, python-format +msgid "%s() (in module %s)" +msgstr "" + +#: sphinx/directives/desc.py:29 +#, python-format +msgid "%s (built-in variable)" +msgstr "" + +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 +#, python-format +msgid "%s (in module %s)" +msgstr "" + +#: sphinx/directives/desc.py:33 +#, python-format +msgid "%s (built-in class)" +msgstr "" + +#: sphinx/directives/desc.py:34 +#, python-format +msgid "%s (class in %s)" +msgstr "" + +#: sphinx/directives/desc.py:46 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "" + +#: sphinx/directives/desc.py:48 +#, python-format +msgid "%s() (%s method)" +msgstr "" + +#: sphinx/directives/desc.py:58 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "" + +#: sphinx/directives/desc.py:60 +#, python-format +msgid "%s() (%s static method)" +msgstr "" + +#: sphinx/directives/desc.py:82 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "" + +#: sphinx/directives/desc.py:84 +#, python-format +msgid "%s (%s attribute)" +msgstr "" + +#: sphinx/directives/desc.py:86 +#, python-format +msgid "%s (C function)" +msgstr "" + +#: sphinx/directives/desc.py:88 +#, python-format +msgid "%s (C member)" +msgstr "" + +#: sphinx/directives/desc.py:90 +#, python-format +msgid "%s (C macro)" +msgstr "" + +#: sphinx/directives/desc.py:92 +#, python-format +msgid "%s (C type)" +msgstr "" + +#: sphinx/directives/desc.py:94 +#, python-format +msgid "%s (C variable)" +msgstr "" + +#: sphinx/directives/desc.py:112 +msgid "Raises" +msgstr "" + +#: sphinx/directives/desc.py:116 +msgid "Variable" +msgstr "" + +#: sphinx/directives/desc.py:119 +msgid "Returns" +msgstr "" + +#: sphinx/directives/desc.py:128 +msgid "Return type" +msgstr "" + +#: sphinx/directives/desc.py:213 +msgid "Parameter" +msgstr "" + +#: sphinx/directives/desc.py:217 +msgid "Parameters" +msgstr "" + +#: sphinx/directives/desc.py:465 +#, python-format +msgid "%scommand line option; %s" +msgstr "" + +#: sphinx/directives/other.py:101 +msgid "Platforms: " +msgstr "Ympäristö" + +#: sphinx/directives/other.py:106 +#, python-format +msgid "%s (module)" +msgstr "%s (moduuli)" + +#: sphinx/directives/other.py:146 +msgid "Section author: " +msgstr "Luvun kirjoittaja: " + +#: sphinx/directives/other.py:148 +msgid "Module author: " +msgstr "Moduulin kirjoittaja: " + +#: sphinx/directives/other.py:150 +msgid "Author: " +msgstr "Tekijä: " + +#: sphinx/directives/other.py:249 +msgid "See also" +msgstr "Katso myös" + +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + +#: sphinx/ext/todo.py:31 +msgid "Todo" +msgstr "Tehtävä vielä" + +#: sphinx/ext/todo.py:75 +#, python-format +msgid "(The original entry is located in %s, line %d and can be found " +msgstr "" + +#: sphinx/ext/todo.py:81 +msgid "here" +msgstr "tässä" + +#: sphinx/locale/__init__.py:15 +msgid "Attention" +msgstr "Huom" + +#: sphinx/locale/__init__.py:16 +msgid "Caution" +msgstr "Varoitus" + +#: sphinx/locale/__init__.py:17 +msgid "Danger" +msgstr "Vaara" + +#: sphinx/locale/__init__.py:18 +msgid "Error" +msgstr "Virhe" + +#: sphinx/locale/__init__.py:19 +msgid "Hint" +msgstr "Vihje" + +#: sphinx/locale/__init__.py:20 +msgid "Important" +msgstr "Tärkeä" + +#: sphinx/locale/__init__.py:21 +msgid "Note" +msgstr "Muista" + +#: sphinx/locale/__init__.py:22 +msgid "See Also" +msgstr "Katso myös" + +#: sphinx/locale/__init__.py:23 +msgid "Tip" +msgstr "Vihje" + +#: sphinx/locale/__init__.py:24 +msgid "Warning" +msgstr "Varoitus" + +#: sphinx/locale/__init__.py:28 +#, python-format +msgid "New in version %s" +msgstr "Uusi versiossa %s" + +#: sphinx/locale/__init__.py:29 +#, python-format +msgid "Changed in version %s" +msgstr "Muutettu versiossa %s" + +#: sphinx/locale/__init__.py:30 +#, python-format +msgid "Deprecated since version %s" +msgstr "Poistettu versiosta %s alkaen" + +#: sphinx/locale/__init__.py:34 +msgid "module" +msgstr "moduuli" +#msgstr "osa" + +#: sphinx/locale/__init__.py:35 +msgid "keyword" +msgstr "" +#msgstr "avainsana" + +#: sphinx/locale/__init__.py:36 +msgid "operator" +msgstr "" +#msgstr "operaattori" + + +#: sphinx/locale/__init__.py:37 +msgid "object" +msgstr "" +#msgstr "objekti" + +#: sphinx/locale/__init__.py:38 +msgid "exception" +msgstr "" +#msgstr "poikkeus" + +#: sphinx/locale/__init__.py:39 +msgid "statement" +msgstr "" + +#: sphinx/locale/__init__.py:40 +msgid "built-in function" +msgstr "" + +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "" + +#: sphinx/static/doctools.js:174 +msgid "Hide Search Matches" +msgstr "Piilota löydetyt" + +#: sphinx/static/searchtools.js:274 +msgid "Searching" +msgstr "Etsitään" + +#: sphinx/static/searchtools.js:279 +msgid "Preparing search..." +msgstr "Valmistellaan etsintää..." + +#: sphinx/static/searchtools.js:338 +msgid "module, in " +msgstr "" + +#: sphinx/static/searchtools.js:347 +msgid ", in " +msgstr "" + +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 +msgid "Search Results" +msgstr "Etsinnän tulos" + +#: sphinx/static/searchtools.js:455 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "Ei löytynyt yhtään. Tarkista hakuehdot, sanahaku, ei sen osia" + +#: sphinx/static/searchtools.js:457 +#, python-format +msgid "Search finished, found %s page(s) matching the search query." +msgstr "Etsintä tehty, löydetty %s sivu(a)." + +#: sphinx/templates/defindex.html:2 +msgid "Overview" +msgstr "Yhteenveto" + +#: sphinx/templates/defindex.html:11 +msgid "Indices and tables:" +msgstr "" + +#: sphinx/templates/defindex.html:14 +msgid "Complete Table of Contents" +msgstr "" + +#: sphinx/templates/defindex.html:15 +msgid "lists all sections and subsections" +msgstr "" + +#: sphinx/templates/defindex.html:17 +msgid "search this documentation" +msgstr "" + +#: sphinx/templates/defindex.html:20 +msgid "quick access to all modules" +msgstr "" + +#: sphinx/templates/defindex.html:22 +msgid "all functions, classes, terms" +msgstr "" + +#: sphinx/templates/genindex-single.html:5 +#, python-format +msgid "Index – %(key)s" +msgstr "" + +#: sphinx/templates/genindex-single.html:44 +#: sphinx/templates/genindex-split.html:14 +#: sphinx/templates/genindex-split.html:27 sphinx/templates/genindex.html:54 +msgid "Full index on one page" +msgstr "Hakemisto yhtenä luettelona" + +#: sphinx/templates/genindex-split.html:7 +msgid "Index pages by letter" +msgstr "Hakemisto aakkostus sivuttain" + +#: sphinx/templates/genindex-split.html:15 +msgid "can be huge" +msgstr "voi olla iso" + +#: sphinx/templates/layout.html:9 +msgid "Navigation" +msgstr "Navikointi" + +#: sphinx/templates/layout.html:40 +msgid "Table Of Contents" +msgstr "Sisällysluettelo" + +#: sphinx/templates/layout.html:46 +msgid "Previous topic" +msgstr "<<" + +#: sphinx/templates/layout.html:48 +msgid "previous chapter" +msgstr "<<" + +#: sphinx/templates/layout.html:51 +msgid "Next topic" +msgstr ">>" + +#: sphinx/templates/layout.html:53 +msgid "next chapter" +msgstr ">>" + +#: sphinx/templates/layout.html:58 +msgid "This Page" +msgstr "Tämä sivu" + +#: sphinx/templates/layout.html:61 +msgid "Show Source" +msgstr "Näytä lähdekoodina" + +#: sphinx/templates/layout.html:71 +msgid "Quick search" +msgstr "Pikahaku" + +#: sphinx/templates/layout.html:74 +msgid "Go" +msgstr "Siirry" + +#: sphinx/templates/layout.html:78 +msgid "Enter search terms or a module, class or function name." +msgstr "Anna etsittävä termi tai moduuli, luokka tai funktio" + +#: sphinx/templates/layout.html:115 +#, python-format +msgid "Search within %(docstitle)s" +msgstr "" + +#: sphinx/templates/layout.html:124 +msgid "About these documents" +msgstr "Tietoja tästä documentistä" + +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 +#: sphinx/templates/search.html:5 +msgid "Search" +msgstr "Etsi" + +#: sphinx/templates/layout.html:129 +msgid "Copyright" +msgstr "" +#msgstr "(c)" + +#: sphinx/templates/layout.html:174 +#, python-format +msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +msgstr "" +#msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +#msgstr "© <a href=\"%(path)s\">kaikki pidätetään</A> %(copyright)." + +#: sphinx/templates/layout.html:176 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "" +##msgstr "© Copyright %(copyright)s." +#msgstr "© %(copyright)." + +#: sphinx/templates/layout.html:179 +#, python-format +msgid "Last updated on %(last_updated)s." +msgstr "" +#msgstr "Viimeksi muutettu %(last_updated)." + +#: sphinx/templates/layout.html:182 +#, python-format +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr """ +#msgstr "Tehty <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)" + +#: sphinx/templates/modindex.html:36 +msgid "Deprecated" +msgstr "Poistettu" + +#: sphinx/templates/opensearch.xml:4 +#, python-format +msgid "Search %(docstitle)s" +msgstr "" + +#: sphinx/templates/search.html:9 +msgid "" +"Please activate JavaScript to enable the search\n" +" functionality." +msgstr "Javascript pitää olla sallittu, jotta etsintä toimii." + +#: sphinx/templates/search.html:14 +msgid "" +"From here you can search these documents. Enter your search\n" +" words into the box below and click \"search\". Note that the search\n" +" function will automatically search for all of the words. Pages\n" +" containing fewer words won't appear in the result list." +msgstr "Anna hakusanat kokonaan, osasanoilla ei haeta." + +#: sphinx/templates/search.html:21 +msgid "search" +msgstr "etsi" + +#: sphinx/templates/search.html:27 +msgid "Your search did not match any results." +msgstr "Ei löytynyt ko. ehdoilla yhtään." + +#: sphinx/templates/changes/frameset.html:5 +#: sphinx/templates/changes/versionchanges.html:12 +#, python-format +msgid "Changes in Version %(version)s — %(docstitle)s" +msgstr "Muutos versiosta %(version) — %(docstitle)" + +#: sphinx/templates/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "" + +#: sphinx/templates/changes/versionchanges.html:17 +#, python-format +msgid "Automatically generated list of changes in version %(version)s" +msgstr "Automaattisesti luotu muutoshistoria alkaen versiosta %(version)" + +#: sphinx/templates/changes/versionchanges.html:18 +msgid "Library changes" +msgstr "" + +#: sphinx/templates/changes/versionchanges.html:23 +msgid "C API changes" +msgstr "" + +#: sphinx/templates/changes/versionchanges.html:25 +msgid "Other changes" +msgstr "" + +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Ympäristö: %s" +msgstr "" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "" + diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.mo b/sphinx/locale/fr/LC_MESSAGES/sphinx.mo Binary files differindex 158e964a..c7b2474d 100644 --- a/sphinx/locale/fr/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.po b/sphinx/locale/fr/LC_MESSAGES/sphinx.po index 0d92cc35..bd6ea6ec 100644 --- a/sphinx/locale/fr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.po @@ -9,93 +9,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: larlet@gmail.com\n" "POT-Creation-Date: 2008-08-08 12:39+0000\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Sébastien Douche <sdouche@gmail.com>\n" "Language-Team: French Translation Team <sphinx-dev@googlegroups.com>\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d %b %Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Index général" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "index" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Index général des modules" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "modules" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "suivant" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "précédent" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "(dans" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Fonctions de base" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Module" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d %B %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Index" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Index du module" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Page de recherche" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Lien permanent vers cette définition" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Lien permanent vers ce titre" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Version" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "variable d'environnement; %s" @@ -105,14 +49,48 @@ msgstr "variable d'environnement; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Fonctions de base" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Module" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Plateforme : %s" +msgid "%b %d, %Y" +msgstr "%d %b %Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[image]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Index général" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "index" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Index général des modules" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "modules" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "suivant" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "précédent" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "(dans" #: sphinx/directives/desc.py:25 #, python-format @@ -130,7 +108,7 @@ msgstr "%s() (dans le module %s)" msgid "%s (built-in variable)" msgstr "%s (variable de base)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (dans le module %s)" @@ -165,62 +143,67 @@ msgstr "%s() (méthode statique %s.%s)" msgid "%s() (%s static method)" msgstr "%s() (méthode statique %s)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (attribut %s.%s)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (attribut %s)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (fonction C)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (membre C)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (macro C)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (type C)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (variable C)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Lève" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Variable" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Retourne" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Type retourné" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Paramètres" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Paramètres" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "%soption de ligne de commande; %s" @@ -246,10 +229,20 @@ msgstr "Auteur du module : " msgid "Author: " msgstr "Auteur : " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Voir aussi" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "A faire" @@ -346,6 +339,14 @@ msgstr "état" msgid "built-in function" msgstr "fonction de base" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Lien permanent vers ce titre" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Lien permanent vers cette définition" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Cacher les résultats de la recherche" @@ -366,11 +367,11 @@ msgstr "module, dans" msgid ", in " msgstr ", dans" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Résultats de la recherche" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -379,7 +380,7 @@ msgstr "" "des termes de recherche et que vous avez sélectionné suffisamment de " "catégories." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "La recherche est terminée, %s page(s) correspond(ent) à la requête." @@ -443,27 +444,23 @@ msgstr "Table des matières" msgid "Previous topic" msgstr "Sujet précédent" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "Chapitre précédent" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Sujet suivant" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "Chapitre suivant" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Cette page" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Suggérer une modification" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Montrer la source" @@ -471,52 +468,49 @@ msgstr "Montrer la source" msgid "Quick search" msgstr "Recherche rapide" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Recherche par mot-clé" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Go" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Saisissez un nom de module, classe ou fonction." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Recherchez dans %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "À propos de ces documents" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Recherche" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Mis à jour le %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -525,15 +519,7 @@ msgstr "" "Créé avec <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Modules les plus utilisés :" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "N'afficher que les modules disponibles sur ces plateformes" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Obsolète" @@ -542,17 +528,13 @@ msgstr "Obsolète" msgid "Search %(docstitle)s" msgstr "Rechercher %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Note :</strong> Vous tentez d'accéder à une ancienne URL de ce " -"serveur. Nous avons essayé de vous rediriger vers la nouvelle adresse de " -"cette page, mais ce n'est peut-être pas la bonne." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -568,11 +550,11 @@ msgstr "" " contenant moins de mots n'apparaîtront pas dans la liste des " "résultats." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "rechercher" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Votre recherche n'a retourné aucun résultat" @@ -604,3 +586,16 @@ msgstr "Modifications de l'API C" msgid "Other changes" msgstr "Autres modifications" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Version" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Plateforme : %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[image]" + diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.js b/sphinx/locale/it/LC_MESSAGES/sphinx.js new file mode 100644 index 00000000..91a939a6 --- /dev/null +++ b/sphinx/locale/it/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "it", "plural_expr": "(n != 1)", "messages": {"module, in ": "modulo, in", "Preparing search...": "Preparazione della ricerca", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "La tua ricerca non ha trovato alcun risultato. Controlla la correttezza dei termini di ricerca e di avere selezionato un numero sufficiente di categorie", "Search finished, found %s page(s) matching the search query.": "Ricerca terminata, trovate %s pagine corrispondenti alla ricerca.", ", in ": ", in ", "Permalink to this headline": "link permanente per questa intestazione", "Searching": "Ricerca in corso", "Permalink to this definition": "link permanente per questa definizione", "Hide Search Matches": "Nascondi i risultati della ricerca", "Search Results": "Risultati della ricerca"}});
\ No newline at end of file diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.mo b/sphinx/locale/it/LC_MESSAGES/sphinx.mo Binary files differnew file mode 100644 index 00000000..4f95d152 --- /dev/null +++ b/sphinx/locale/it/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.po b/sphinx/locale/it/LC_MESSAGES/sphinx.po new file mode 100644 index 00000000..c6db1830 --- /dev/null +++ b/sphinx/locale/it/LC_MESSAGES/sphinx.po @@ -0,0 +1,595 @@ +# Italian translations for Sphinx. +# Copyright (C) 2008 ORGANIZATION +# This file is distributed under the same license as the Sphinx project. +# Sandro Dentella <sandro@e-den.it>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: Sphinx 0.5\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2008-11-27 18:39+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" +"Last-Translator: Sandro Dentella <sandro@e-den.it>\n" +"Language-Team: <sphinx-dev@googlegroups.com>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.3\n" + +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 +#, python-format +msgid "%B %d, %Y" +msgstr "%d %B %Y" + +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 +#: sphinx/templates/genindex-split.html:2 +#: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 +#: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 +msgid "Index" +msgstr "Indice" + +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 +msgid "Module Index" +msgstr "Indice dei Moduli" + +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 +msgid "Search Page" +msgstr "Cerca" + +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 +#, python-format +msgid "environment variable; %s" +msgstr "variabile d'ambiente, %s" + +#: sphinx/roles.py:60 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" +msgstr "Python Enhancement Proposals!PEP %s" + +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Builtin" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Modulo" + +#: sphinx/builders/html.py:118 +#, python-format +msgid "%b %d, %Y" +msgstr "%d/%b/%Y" + +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Indice generale" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "indice" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Indice dei moduli" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "moduli" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "successivo" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "precedente" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr " (in " + +#: sphinx/directives/desc.py:25 +#, python-format +msgid "%s() (built-in function)" +msgstr "%s() (funzione built-in)" + +#: sphinx/directives/desc.py:26 sphinx/directives/desc.py:42 +#: sphinx/directives/desc.py:54 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (nel modulo %s)" + +#: sphinx/directives/desc.py:29 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (variabile built-in)" + +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (nel modulo %s)" + +#: sphinx/directives/desc.py:33 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (classe built-in)" + +#: sphinx/directives/desc.py:34 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (classe in %s)" + +#: sphinx/directives/desc.py:46 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s() (%s.%s metodo)" + +#: sphinx/directives/desc.py:48 +#, python-format +msgid "%s() (%s method)" +msgstr "%s() (%s metodo)" + +#: sphinx/directives/desc.py:58 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s() (%s.%s metodo statico)" + +#: sphinx/directives/desc.py:60 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (%s metodo statico)" + +#: sphinx/directives/desc.py:82 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (%s.%s attributo)" + +#: sphinx/directives/desc.py:84 +#, python-format +msgid "%s (%s attribute)" +msgstr "%s (%s attributo)" + +#: sphinx/directives/desc.py:86 +#, python-format +msgid "%s (C function)" +msgstr "%s (funzione C)" + +#: sphinx/directives/desc.py:88 +#, python-format +msgid "%s (C member)" +msgstr "%s (membro C )" + +#: sphinx/directives/desc.py:90 +#, python-format +msgid "%s (C macro)" +msgstr "%s (macro C)" + +#: sphinx/directives/desc.py:92 +#, python-format +msgid "%s (C type)" +msgstr "%s (tipo C)" + +#: sphinx/directives/desc.py:94 +#, python-format +msgid "%s (C variable)" +msgstr "%s (variabile C)" + +#: sphinx/directives/desc.py:112 +msgid "Raises" +msgstr "Solleva" + +#: sphinx/directives/desc.py:116 +msgid "Variable" +msgstr "Variabile" + +#: sphinx/directives/desc.py:119 +msgid "Returns" +msgstr "Ritorna" + +#: sphinx/directives/desc.py:128 +msgid "Return type" +msgstr "Tipo di ritorno" + +#: sphinx/directives/desc.py:213 +msgid "Parameter" +msgstr "Parametri" + +#: sphinx/directives/desc.py:217 +msgid "Parameters" +msgstr "Parametri" + +#: sphinx/directives/desc.py:465 +#, python-format +msgid "%scommand line option; %s" +msgstr "%sopzione di linea di comando; %s" + +#: sphinx/directives/other.py:101 +msgid "Platforms: " +msgstr "Piattaforme:" + +#: sphinx/directives/other.py:106 +#, python-format +msgid "%s (module)" +msgstr "%s (modulo)" + +#: sphinx/directives/other.py:146 +msgid "Section author: " +msgstr "Autore della sezione" + +#: sphinx/directives/other.py:148 +msgid "Module author: " +msgstr "Autore del modulo" + +#: sphinx/directives/other.py:150 +msgid "Author: " +msgstr "Autore: " + +#: sphinx/directives/other.py:249 +msgid "See also" +msgstr "Vedi anche" + +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "alias per :class:`%s`" + +#: sphinx/ext/todo.py:31 +msgid "Todo" +msgstr "Da fare" + +#: sphinx/ext/todo.py:75 +#, python-format +msgid "(The original entry is located in %s, line %d and can be found " +msgstr "(La riga originale si trova in %s, linea %d e può essere trovata " + +#: sphinx/ext/todo.py:81 +msgid "here" +msgstr "qui" + +#: sphinx/locale/__init__.py:15 +msgid "Attention" +msgstr "Attenzione" + +#: sphinx/locale/__init__.py:16 +msgid "Caution" +msgstr "Attenzione" + +#: sphinx/locale/__init__.py:17 +msgid "Danger" +msgstr "Pericolo" + +#: sphinx/locale/__init__.py:18 +msgid "Error" +msgstr "Errore" + +#: sphinx/locale/__init__.py:19 +msgid "Hint" +msgstr "Consiglio" + +#: sphinx/locale/__init__.py:20 +msgid "Important" +msgstr "Importante" + +#: sphinx/locale/__init__.py:21 +msgid "Note" +msgstr "Nota" + +#: sphinx/locale/__init__.py:22 +msgid "See Also" +msgstr "Vedi anche" + +#: sphinx/locale/__init__.py:23 +msgid "Tip" +msgstr "Suggerimento" + +#: sphinx/locale/__init__.py:24 +msgid "Warning" +msgstr "Avvertimento" + +#: sphinx/locale/__init__.py:28 +#, python-format +msgid "New in version %s" +msgstr "Nuovo nella versione %s" + +#: sphinx/locale/__init__.py:29 +#, python-format +msgid "Changed in version %s" +msgstr "Cambiato nella versione %s" + +#: sphinx/locale/__init__.py:30 +#, python-format +msgid "Deprecated since version %s" +msgstr "Deprecato dalla versione %s" + +#: sphinx/locale/__init__.py:34 +msgid "module" +msgstr "modulo" + +#: sphinx/locale/__init__.py:35 +msgid "keyword" +msgstr "keyword" + +#: sphinx/locale/__init__.py:36 +msgid "operator" +msgstr "operatore" + +#: sphinx/locale/__init__.py:37 +msgid "object" +msgstr "oggetto" + +#: sphinx/locale/__init__.py:38 +msgid "exception" +msgstr "eccezione" + +#: sphinx/locale/__init__.py:39 +msgid "statement" +msgstr "statement" + +#: sphinx/locale/__init__.py:40 +msgid "built-in function" +msgstr "funzione built-in" + +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "link permanente per questa intestazione" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "link permanente per questa definizione" + +#: sphinx/static/doctools.js:174 +msgid "Hide Search Matches" +msgstr "Nascondi i risultati della ricerca" + +#: sphinx/static/searchtools.js:274 +msgid "Searching" +msgstr "Ricerca in corso" + +#: sphinx/static/searchtools.js:279 +msgid "Preparing search..." +msgstr "Preparazione della ricerca" + +#: sphinx/static/searchtools.js:338 +msgid "module, in " +msgstr "modulo, in" + +#: sphinx/static/searchtools.js:347 +msgid ", in " +msgstr ", in " + +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 +msgid "Search Results" +msgstr "Risultati della ricerca" + +#: sphinx/static/searchtools.js:455 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"La tua ricerca non ha trovato alcun risultato. Controlla la correttezza " +"dei termini di ricerca e di avere selezionato un numero sufficiente di " +"categorie" + +#: sphinx/static/searchtools.js:457 +#, python-format +msgid "Search finished, found %s page(s) matching the search query." +msgstr "Ricerca terminata, trovate %s pagine corrispondenti alla ricerca." + +#: sphinx/templates/defindex.html:2 +msgid "Overview" +msgstr "Sintesi" + +#: sphinx/templates/defindex.html:11 +msgid "Indices and tables:" +msgstr "Indici e tabelle:" + +#: sphinx/templates/defindex.html:14 +msgid "Complete Table of Contents" +msgstr "Tabella dei contenuti completa" + +#: sphinx/templates/defindex.html:15 +msgid "lists all sections and subsections" +msgstr "elenca l'insieme delle sezioni e sottosezioni" + +#: sphinx/templates/defindex.html:17 +msgid "search this documentation" +msgstr "cerca in questa documentazione" + +#: sphinx/templates/defindex.html:20 +msgid "quick access to all modules" +msgstr "accesso veloce ai moduli" + +#: sphinx/templates/defindex.html:22 +msgid "all functions, classes, terms" +msgstr "tutte le funzioni, classi e moduli" + +#: sphinx/templates/genindex-single.html:5 +#, python-format +msgid "Index – %(key)s" +msgstr "Indice – %(key)s" + +#: sphinx/templates/genindex-single.html:44 +#: sphinx/templates/genindex-split.html:14 +#: sphinx/templates/genindex-split.html:27 sphinx/templates/genindex.html:54 +msgid "Full index on one page" +msgstr "Indice completo in una pagina" + +#: sphinx/templates/genindex-split.html:7 +msgid "Index pages by letter" +msgstr "Indice delle pagine per lettera" + +#: sphinx/templates/genindex-split.html:15 +msgid "can be huge" +msgstr "può essere enorme" + +#: sphinx/templates/layout.html:9 +msgid "Navigation" +msgstr "Navigazione" + +#: sphinx/templates/layout.html:40 +msgid "Table Of Contents" +msgstr "Tabella dei contenuti" + +#: sphinx/templates/layout.html:46 +msgid "Previous topic" +msgstr "Argomento precedente" + +#: sphinx/templates/layout.html:48 +msgid "previous chapter" +msgstr "capitolo precedente" + +#: sphinx/templates/layout.html:51 +msgid "Next topic" +msgstr "Argomento successivo" + +#: sphinx/templates/layout.html:53 +msgid "next chapter" +msgstr "capitolo successivo" + +#: sphinx/templates/layout.html:58 +msgid "This Page" +msgstr "Questa pagina" + +#: sphinx/templates/layout.html:61 +msgid "Show Source" +msgstr "Mostra sorgente" + +#: sphinx/templates/layout.html:71 +msgid "Quick search" +msgstr "Ricerca veloce" + +#: sphinx/templates/layout.html:74 +msgid "Go" +msgstr "Vai" + +#: sphinx/templates/layout.html:78 +msgid "Enter search terms or a module, class or function name." +msgstr "Inserisci un termine di ricerca un modulo, classe o nome di funzione" + +#: sphinx/templates/layout.html:115 +#, python-format +msgid "Search within %(docstitle)s" +msgstr "Cerca in %(docstitle)s" + +#: sphinx/templates/layout.html:124 +msgid "About these documents" +msgstr "A proposito di questi documenti" + +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 +#: sphinx/templates/search.html:5 +msgid "Search" +msgstr "Cerca" + +#: sphinx/templates/layout.html:129 +msgid "Copyright" +msgstr "Copyright" + +#: sphinx/templates/layout.html:174 +#, python-format +msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." + +#: sphinx/templates/layout.html:176 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "© Copyright %(copyright)s." + +#: sphinx/templates/layout.html:179 +#, python-format +msgid "Last updated on %(last_updated)s." +msgstr "Ultimo Aggiornamento on %(last_updated)s." + +#: sphinx/templates/layout.html:182 +#, python-format +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"Creato con <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." + +#: sphinx/templates/modindex.html:36 +msgid "Deprecated" +msgstr "Deprecato" + +#: sphinx/templates/opensearch.xml:4 +#, python-format +msgid "Search %(docstitle)s" +msgstr "Cerca %(docstitle)s" + +#: sphinx/templates/search.html:9 +msgid "" +"Please activate JavaScript to enable the search\n" +" functionality." +msgstr "" + +#: sphinx/templates/search.html:14 +msgid "" +"From here you can search these documents. Enter your search\n" +" words into the box below and click \"search\". Note that the search\n" +" function will automatically search for all of the words. Pages\n" +" containing fewer words won't appear in the result list." +msgstr "" +"Puoi effettuare una ricerca in questi documenti. Immetti le parole chiave" +" \n" +" della tua ricerca nel riquadro sottostante \"cerca\". Nota che la " +"funzione\n" +" di ricerca cerca automaticamente per tutte le parole. Le pagine\n" +" che contendono meno parole non compariranno nei risultati di ricerca." + +#: sphinx/templates/search.html:21 +msgid "search" +msgstr "cerca" + +#: sphinx/templates/search.html:27 +msgid "Your search did not match any results." +msgstr "La tua ricerca non ha ottenuto risultati" + +#: sphinx/templates/changes/frameset.html:5 +#: sphinx/templates/changes/versionchanges.html:12 +#, python-format +msgid "Changes in Version %(version)s — %(docstitle)s" +msgstr "Modifiche nella Versione %(version)s — %(docstitle)s" + +#: sphinx/templates/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "%(filename)s — %(docstitle)s" + +#: sphinx/templates/changes/versionchanges.html:17 +#, python-format +msgid "Automatically generated list of changes in version %(version)s" +msgstr "Lista delle modifiche generata automaticamente nella versione %(version)s" + +#: sphinx/templates/changes/versionchanges.html:18 +msgid "Library changes" +msgstr "Modifiche nella libreria" + +#: sphinx/templates/changes/versionchanges.html:23 +msgid "C API changes" +msgstr "Modifiche nelle API C" + +#: sphinx/templates/changes/versionchanges.html:25 +msgid "Other changes" +msgstr "Altre modifiche" + +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Release" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Piattaforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[immagine]" + diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo Binary files differindex 66b1aa9d..36735ab5 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po index 3bec3ae3..307a8700 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po @@ -8,93 +8,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Yasushi MASUDA <whosaysni@gmail.com>\n" "Language-Team: ja <LL@li.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%Y 年 %m 月 %d 日" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "総合索引" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "索引" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "モジュール総索引" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "モジュール" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "次へ" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "前へ" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "組み込み" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "モジュールレベル" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "索引" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "モジュール索引" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "検索ページ" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "この定義へのパーマリンク" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "このヘッドラインへのパーマリンク" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "リリース" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "環境変数; %s" @@ -104,14 +48,48 @@ msgstr "環境変数; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "組み込み" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "モジュールレベル" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "プラットフォーム: %s" +msgid "%b %d, %Y" +msgstr "%Y 年 %m 月 %d 日" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[画像]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "総合索引" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "索引" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "モジュール総索引" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "モジュール" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "次へ" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "前へ" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" #: sphinx/directives/desc.py:25 #, python-format @@ -129,7 +107,7 @@ msgstr "%s() (%s モジュール)" msgid "%s (built-in variable)" msgstr "%s (組み込み変数)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (%s モジュール)" @@ -164,62 +142,67 @@ msgstr "%s() (%s.%s の静的メソッド)" msgid "%s() (%s static method)" msgstr "%s() (%s の静的メソッド)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s の属性)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s の属性)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C の関数)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (C のメンバ変数)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C のマクロ)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C のデータ型)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C の変数)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "例外" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "変数" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "戻り値" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "戻り値の型" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "パラメタ" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "パラメタ" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, fuzzy, python-format msgid "%scommand line option; %s" msgstr "%sコマンドラインオプション; %s" @@ -245,10 +228,20 @@ msgstr "モジュールの作者: " msgid "Author: " msgstr "作者: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "参考" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "" @@ -345,6 +338,14 @@ msgstr "文" msgid "built-in function" msgstr "組み込み関数" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "このヘッドラインへのパーマリンク" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "この定義へのパーマリンク" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "検索結果を隠す" @@ -366,17 +367,17 @@ msgstr "モジュール" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "検索結果" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "検索条件に一致するドキュメントはありませんでした。検索したい言葉を正しいつづりで入力しているか確認してください。また、正しいカテゴリの検索を行っているか確認してください。" -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "検索が終了し、条件に一致するページが %s 個みつかりました。" @@ -440,27 +441,23 @@ msgstr "目次" msgid "Previous topic" msgstr "前のトピックへ" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "前の章へ" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "次のトピックへ" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "次の章へ" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "このページ" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "変更のサジェスト" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "ソースコードを表示" @@ -468,52 +465,49 @@ msgstr "ソースコードを表示" msgid "Quick search" msgstr "クイック検索" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "キーワード検索" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "検索" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "モジュール、クラス、または関数名を入力してください" -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "%(docstitle)s 内を検索" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "このドキュメントについて" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "検索" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "著作権" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "最終更新: %(last_updated)s" -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -522,15 +516,7 @@ msgstr "" "このドキュメントは <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s で生成しました。" -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "よく参照されているモジュール:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "このプラットフォームで利用可能なモジュールだけを表示する" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "撤廃" @@ -539,16 +525,13 @@ msgstr "撤廃" msgid "Search %(docstitle)s" msgstr "%(docstitle)s 内を検索" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>注意:</strong> あなたが表示しようとしているのは古い URL です。このページに対応する新しい URL " -"へのリダイレクトを試みますが、適切なリダイレクト先でないかもしれないので注意してください。" -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -557,11 +540,11 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "このページからドキュメントを検索できます。キーワードを下のボックスに入力して、「検索」をクリックしてください。入力された全てのキーワードを含むページが検索されます。一部のキーワードしか含まないページは検索結果に表示されないので注意してください。" -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "検索" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "検索条件に一致する項目がありませんでした。" @@ -593,3 +576,16 @@ msgstr "C API に関する変更" msgid "Other changes" msgstr "その多の変更" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "リリース" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "プラットフォーム: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[画像]" + diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.mo b/sphinx/locale/nl/LC_MESSAGES/sphinx.mo Binary files differindex 583a2993..035fd0e3 100644 --- a/sphinx/locale/nl/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/nl/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.po b/sphinx/locale/nl/LC_MESSAGES/sphinx.po index 49b2d192..1dbc4399 100644 --- a/sphinx/locale/nl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/nl/LC_MESSAGES/sphinx.po @@ -7,93 +7,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: nl <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d.%b.%Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Algemene index" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "Index" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Globale Module-index" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "modules" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "volgende" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "vorige" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Builtins" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Moduleniveau" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d. %B %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Index" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Module-index" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Zoekpagina" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Permanente link naar deze definitie" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Permanente link naar deze titel" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Release" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "Omgevingsvariabele; %s" @@ -103,14 +47,48 @@ msgstr "Omgevingsvariabele; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Builtins" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Moduleniveau" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Platform: %s" +msgid "%b %d, %Y" +msgstr "%d.%b.%Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[afbeelding]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Algemene index" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "Index" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Globale Module-index" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "modules" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "volgende" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "vorige" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" #: sphinx/directives/desc.py:25 #, python-format @@ -128,7 +106,7 @@ msgstr "%s() (in module %s)" msgid "%s (built-in variable)" msgstr "%s (geïntegreerde variabele)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (in module %s)" @@ -163,62 +141,67 @@ msgstr "%s() (%s.%s statische methode)" msgid "%s() (%s static method)" msgstr "%s() (%s statische methode)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s attribuut)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s attribuut)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C-functie)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (C member)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C-macro)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C type)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C-variabele)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Veroorzaakt" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Variabele" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Returns" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Return type" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parameters" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parameters" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, fuzzy, python-format msgid "%scommand line option; %s" msgstr "%scommandolijn optie; %s" @@ -244,10 +227,20 @@ msgstr "Auteur van deze module: " msgid "Author: " msgstr "Auteur: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Zie ook" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "" @@ -344,6 +337,14 @@ msgstr "statement" msgid "built-in function" msgstr "geïntegreerde functie" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Permanente link naar deze titel" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Permanente link naar deze definitie" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Zoekresultaten verbergen" @@ -365,11 +366,11 @@ msgstr "module" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Zoekresultaten" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -377,7 +378,7 @@ msgstr "" "Uw zoekopdracht leverde geen resultaten op. Controleer of alle " "woordencorrect gespeld zijn en dat u genoeg categoriën hebt geselecteerd." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Zoeken voltooid, %s pagina(s) gevonden." @@ -441,27 +442,23 @@ msgstr "Inhoudstafel" msgid "Previous topic" msgstr "Vorig onderwerp" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "Vorig hoofdstuk" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Volgend onderwerp" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "volgend hoofdstuk" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Deze Pagina" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Wijziging Voorstellen" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Broncode weergeven" @@ -469,52 +466,49 @@ msgstr "Broncode weergeven" msgid "Quick search" msgstr "Snel zoeken" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Trefwoord opzoeken" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Go" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Geef de naam van een module, klasse of functie." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Zoeken in %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "Over deze documenten" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Zoeken" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Laatste aanpassing op %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -523,15 +517,7 @@ msgstr "" "Aangemaakt met <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Populairste modules:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Enkel modules weergeven die op deze platformen beschikbaar zijn" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Verouderd" @@ -540,17 +526,13 @@ msgstr "Verouderd" msgid "Search %(docstitle)s" msgstr "Zoeken %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Opgelet:</strong> U heeft een verouderde URL aangevraagd op deze " -"server. Wij hebben probeerd u door te verwijzen naar de nieuwe locatie " -"van deze pagina, maar dat is misschien niet gelukt." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -565,11 +547,11 @@ msgstr "" "\n" " zullen niet tussen de resultaten verschijnen." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "zoeken" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Uw zoekopdracht leverde geen resultaten op." @@ -601,3 +583,16 @@ msgstr "Veranderingen in de C-API" msgid "Other changes" msgstr "Andere veranderingen" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Release" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Platform: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[afbeelding]" + diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo Binary files differindex ec1f3602..93fd9f94 100644 --- a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.po b/sphinx/locale/pl/LC_MESSAGES/sphinx.po index 87f5c4cb..990c8e38 100644 --- a/sphinx/locale/pl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.po @@ -4,7 +4,7 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-08-10 11:43+0000\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Michał Kandulski <Michal.Kandulski@poczta.onet.pl>\n" "Language-Team: \n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " @@ -12,86 +12,30 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%b %d %Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Indeks ogólny" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "indeks" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Indeks modułów" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "moduły" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "dalej" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "wstecz" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Wbudowane" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Poziom modułu" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%B %d %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Indeks" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Indeks modułów" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Wyszukiwanie" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Stały odnośnik do tej definicji" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Stały odnośnik do tego nagłówka" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Wydanie" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "zmienna środowiskowa; %s" @@ -101,14 +45,48 @@ msgstr "zmienna środowiskowa; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Wbudowane" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Poziom modułu" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Platforma: %s" +msgid "%b %d, %Y" +msgstr "%b %d %Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[obrazek]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Indeks ogólny" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "indeks" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Indeks modułów" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "moduły" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "dalej" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "wstecz" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" #: sphinx/directives/desc.py:25 #, python-format @@ -126,7 +104,7 @@ msgstr "%s() (w module %s)" msgid "%s (built-in variable)" msgstr "%s (zmienna wbudowana)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (w module %s)" @@ -161,62 +139,67 @@ msgstr "%s() (%s.%s statyczna metoda)" msgid "%s() (%s static method)" msgstr "%s() (%s statyczna metoda)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atrybut)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atrybut)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (funkcja C)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (pole C)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (makro C)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (typ C)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (zmienna C)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Wyrzuca" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Zmienna" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Zwraca" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Typ zwracany" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parametry" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parametry" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, fuzzy, python-format msgid "%scommand line option; %s" msgstr "%sopcja linii komend; %s" @@ -242,10 +225,20 @@ msgstr "Autor modułu: " msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Zobacz także" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "" @@ -342,6 +335,14 @@ msgstr "instrukcja" msgid "built-in function" msgstr "funkcja wbudowana" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Stały odnośnik do tego nagłówka" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Stały odnośnik do tej definicji" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Ukryj wyniki wyszukiwania" @@ -363,11 +364,11 @@ msgstr "moduł" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Wyniki wyszukiwania" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -375,7 +376,7 @@ msgstr "" "Nie znaleziono żadnych pasujących dokumentów. Upewnij się, że wszystkie " "słowa są poprawnie wpisane i że wybrałeś wystarczającąliczbę kategorii." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Przeszukiwanie zakończone, znaleziono %s pasujących stron." @@ -439,27 +440,23 @@ msgstr "Spis treści" msgid "Previous topic" msgstr "Poprzedni temat" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "poprzedni rozdział" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Następny temat" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "następny rozdział" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Ta strona" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Zasugeruj zmianę" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Pokaż źródło" @@ -467,52 +464,49 @@ msgstr "Pokaż źródło" msgid "Quick search" msgstr "Szybkie wyszukiwanie" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Szukanie wg słowa kluczowego" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Szukaj" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Wprowadź nazwę modułu, klasy lub funkcji." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Szukaj pośród %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "O tych dokumentach" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Szukaj" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Ostatnia modyfikacja %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -521,15 +515,7 @@ msgstr "" "Utworzone przy pomocy <a href=\"http://sphinx.pocoo.org/\">Sphinx</a>'a " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Najbardziej popularne moduły:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Pokaż moduły dostępne tylko na tych platformach" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Niezalecane" @@ -538,17 +524,13 @@ msgstr "Niezalecane" msgid "Search %(docstitle)s" msgstr "Przeszukaj %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Uwaga:</strong> Zażądano przedawnionego URL'a z tego serwera. " -"Nastąpiła próba przekierowania do nowej lokalizacji, ale może ona być " -"niewłaściwa." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -562,11 +544,11 @@ msgstr "" "Strony nie zawierające wszystkich słów nie znajdą się na wynikowej " "liście." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "Szukaj" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Nie znaleziono żadnych pasujących stron." @@ -598,3 +580,16 @@ msgstr "Zmiany w C API" msgid "Other changes" msgstr "Inne zmiany" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Wydanie" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Platforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[obrazek]" + diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo Binary files differindex 647d4405..7c470e4f 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po index 021b70aa..48ee96db 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po @@ -8,93 +8,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: roger.demetrescu@gmail.com\n" "POT-Creation-Date: 2008-11-09 19:46+0100\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Roger Demetrescu <roger.demetrescu@gmail.com>\n" "Language-Team: pt_BR <roger.demetrescu@gmail.com>\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d/%m/%Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Índice Geral" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "índice" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Índice Global de Módulos" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "módulos" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "próximo" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "anterior" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr " (em " - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Internos" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Módulo" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d/%m/%Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Índice" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Índice do Módulo" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Página de Pesquisa" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Link permanente para esta definição" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Link permanente para este título" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Versão" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "váriavel de ambiente; %s" @@ -104,14 +48,48 @@ msgstr "váriavel de ambiente; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Internos" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Módulo" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Plataforma: %s" +msgid "%b %d, %Y" +msgstr "%d/%m/%Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[imagem]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Índice Geral" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "índice" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Índice Global de Módulos" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "módulos" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "próximo" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "anterior" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr " (em " #: sphinx/directives/desc.py:25 #, python-format @@ -129,7 +107,7 @@ msgstr "%s() (no módulo %s)" msgid "%s (built-in variable)" msgstr "%s (variável interna)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (no módulo %s)" @@ -164,62 +142,67 @@ msgstr "%s() (método estático %s.%s)" msgid "%s() (%s static method)" msgstr "%s() (método estático %s)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (atributo %s.%s)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (atributo %s)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (função C)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (membro C)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (macro C)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (tipo C)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (variável C)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Levanta" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Variável" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Retorna" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Tipo de retorno" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parâmetros" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parâmetros" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "%sopção de linha de comando; %s" @@ -245,10 +228,20 @@ msgstr "Autor do módulo: " msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Veja também" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "Por fazer" @@ -345,6 +338,14 @@ msgstr "comando" msgid "built-in function" msgstr "função interna" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Link permanente para este título" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Link permanente para esta definição" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Esconder Resultados da Pesquisa" @@ -365,11 +366,11 @@ msgstr "módulo, em " msgid ", in " msgstr ", em " -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Resultados da Pesquisa" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -378,7 +379,7 @@ msgstr "" " todas as palavras foram digitadas corretamente e de que você tenha " "selecionado o mínimo de categorias." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -444,27 +445,23 @@ msgstr "Tabela de Conteúdo" msgid "Previous topic" msgstr "Tópico anterior" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "capítulo anterior" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Próximo tópico" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "próximo capítulo" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Esta Página" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Sugerir Alteração" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Exibir Fonte" @@ -472,52 +469,49 @@ msgstr "Exibir Fonte" msgid "Quick search" msgstr "Pesquisa rápida" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Pesquisa de palavras-chave" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Ir" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Informe o nome de um módulo, classe ou função." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Pesquisar dentro de %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "Sobre estes documentos" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Pesquisar" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Copyright" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Copyright %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Última atualização em %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -526,15 +520,7 @@ msgstr "" "Criado com <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Módulos mais populares:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Exibir somente módulos disponíveis nestas plataformas" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Obsoleto" @@ -543,17 +529,13 @@ msgstr "Obsoleto" msgid "Search %(docstitle)s" msgstr "Pesquisar em %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Nota:</strong> Você requisitou uma URL desatualizada deste " -"servidor. Tentamos redirecioná-lo para um novo endereço desta página, " -"porém é possível que o mesmo não seja o correto." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -568,11 +550,11 @@ msgstr "" " Páginas contendo menos palavras não irão aparecer na lista de " "resultado." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "pesquisar" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Sua pesquisa não encontrou nenhum resultado." @@ -604,3 +586,16 @@ msgstr "Alterações na API C" msgid "Other changes" msgstr "Outras alterações" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Versão" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Plataforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[imagem]" + diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.mo b/sphinx/locale/sl/LC_MESSAGES/sphinx.mo Binary files differindex d5282768..e8cc5a6e 100644 --- a/sphinx/locale/sl/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/sl/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.po b/sphinx/locale/sl/LC_MESSAGES/sphinx.po index 0c6bbab3..f980ee1b 100644 --- a/sphinx/locale/sl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sl/LC_MESSAGES/sphinx.po @@ -4,93 +4,37 @@ msgstr "" "Project-Id-Version: Sphinx\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Rok Garbas <rok.garbas@gmail.com>\n" "Language-Team: Rok Garbas <rok.garbas@gmail.com>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%d %b, %Y" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "Splošni abecedni seznam" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "abecedni seznam" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "Splošen Seznam Modulov" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "Moduli" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "naprej" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "nazaj" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "(v " - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "Vgrajeni deli" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "Nivo modula" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%d %B, %Y" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "Abecedni seznam" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "Seznam modulov" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "Iskalna stran" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "Povezava na to definicijo" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "Povezava na naslov" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "Izdaja" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "globalna spremenljivka; %s" @@ -100,14 +44,48 @@ msgstr "globalna spremenljivka; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "Vgrajeni deli" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "Nivo modula" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "Platforma: %s" +msgid "%b %d, %Y" +msgstr "%d %b, %Y" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[slika]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "Splošni abecedni seznam" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "abecedni seznam" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "Splošen Seznam Modulov" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "Moduli" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "naprej" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "nazaj" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "(v " #: sphinx/directives/desc.py:25 #, python-format @@ -125,7 +103,7 @@ msgstr "%s() (v modulu %s)" msgid "%s (built-in variable)" msgstr "%s (vgrajene spremenljivke)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s (v modulu %s)" @@ -160,62 +138,67 @@ msgstr "%s() (%s.%s statična metoda)" msgid "%s() (%s static method)" msgstr "%s() (%s statična metoda)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atribut)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atribut)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C funkcija)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (C član)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C makro)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C tip)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C spremenljivka)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "Javi" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "Spremenljivka" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "Vrne" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "Vrne tip" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "Parametri" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "Parametri" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "%sopcija komandne linije; %s" @@ -241,10 +224,20 @@ msgstr "Avtor modula:" msgid "Author: " msgstr "Avtor:" -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "Poglej tudi" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "Naredi" @@ -341,6 +334,14 @@ msgstr "izjava" msgid "built-in function" msgstr "vgrajene funkcije" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "Povezava na naslov" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "Povezava na to definicijo" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "Skrij Resultate Iskanja" @@ -361,11 +362,11 @@ msgstr "modul, v " msgid ", in " msgstr ", v " -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "Rezultati Iskanja" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -373,7 +374,7 @@ msgstr "" "Za vaše iskanje ni rezultatov. Prosimo preglejte ali so vse besede " "pravilno črkovane in ali ste izbrali dovolj kategorij." -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Iskanje končano, najdeno %s strani, ki ustrezajo iskalnemu nizu." @@ -437,27 +438,23 @@ msgstr "Seznam Vsebine" msgid "Previous topic" msgstr "Prejšnja tema" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "prejšnje poglavje" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "Naslednja tema" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "naslednje poglavje" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "Ta stran" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "Predlagaj spremembo" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "Prikaži izvorno kodo" @@ -465,52 +462,49 @@ msgstr "Prikaži izvorno kodo" msgid "Quick search" msgstr "Hitro iskanje" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "Iskanje po ključniih besedah" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "Potrdi" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "Vnesi ime mudla, razreda ali funkcije." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "Išči med %(docstitle)s" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "O teh dokumentih" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "Išči" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "Vse pravice pridržane" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Vse pravice pridržane</a> %(copyright)s." -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Vse pravice pridržane %(copyright)s." -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Zadnjič posodobljeno na %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " @@ -519,15 +513,7 @@ msgstr "" "Narejeno s <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "Najbolj popularni moduli:" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "Prikaži module na razpolago na platformah" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "Zastarelo" @@ -536,17 +522,13 @@ msgstr "Zastarelo" msgid "Search %(docstitle)s" msgstr "Išči %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -"<strong>Opomba:</strong> Vaš zahtevek za URL s tega streznika je " -"zastaral. Poskušali smo vas preusmeriti na novo lokacijo, vendar utegne " -"biti napačna." -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -559,11 +541,11 @@ msgstr "" " bo iskalo po vseh besedah v iskalnem nizu. Strani, ki ne\n" " vsebujejo vseh besed ne bodo prikazane na seznamu rezultatov." -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "išči" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "Vaše iskanje ni imelo nobenega zadetka." @@ -595,3 +577,16 @@ msgstr "C API spremembe" msgid "Other changes" msgstr "Ostale spremembe" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "Izdaja" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "Platforma: %s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[slika]" + diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot index ad78625f..657f5df1 100644 --- a/sphinx/locale/sphinx.pot +++ b/sphinx/locale/sphinx.pot @@ -1,116 +1,94 @@ # Translations template for Sphinx. -# Copyright (C) 2008 ORGANIZATION +# Copyright (C) 2009 ORGANIZATION # This file is distributed under the same license as the Sphinx project. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2008. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2009. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Sphinx 0.5\n" +"Project-Id-Version: Sphinx 0.6\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2008-11-27 18:39+0100\n" +"POT-Creation-Date: 2009-01-24 18:39+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format -msgid "%b %d, %Y" -msgstr "" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "" - -#: sphinx/builder.py:427 -msgid "index" +msgid "%B %d, %Y" msgstr "" -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 +#: sphinx/templates/genindex-split.html:2 +#: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 +#: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 +msgid "Index" msgstr "" -#: sphinx/builder.py:429 -msgid "modules" +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 +msgid "Module Index" msgstr "" -#: sphinx/builder.py:466 -msgid "next" +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 +msgid "Search Page" msgstr "" -#: sphinx/builder.py:473 -msgid "previous" +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 +#, python-format +msgid "environment variable; %s" msgstr "" -#: sphinx/builder.py:1054 -msgid " (in " +#: sphinx/roles.py:60 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" msgstr "" -#: sphinx/builder.py:1129 +#: sphinx/builders/changes.py:64 msgid "Builtins" msgstr "" -#: sphinx/builder.py:1131 +#: sphinx/builders/changes.py:66 msgid "Module level" msgstr "" -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/builders/html.py:118 #, python-format -msgid "%B %d, %Y" -msgstr "" - -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 -#: sphinx/templates/genindex-split.html:2 -#: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 -#: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 -msgid "Index" -msgstr "" - -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 -msgid "Module Index" -msgstr "" - -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 -msgid "Search Page" +msgid "%b %d, %Y" msgstr "" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" msgstr "" -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" +#: sphinx/builders/html.py:137 +msgid "index" msgstr "" -#: sphinx/latexwriter.py:172 -msgid "Release" +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" msgstr "" -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 -#, python-format -msgid "environment variable; %s" +#: sphinx/builders/html.py:139 +msgid "modules" msgstr "" -#: sphinx/roles.py:60 -#, python-format -msgid "Python Enhancement Proposals!PEP %s" +#: sphinx/builders/html.py:179 +msgid "next" msgstr "" -#: sphinx/textwriter.py:166 -#, python-format -msgid "Platform: %s" +#: sphinx/builders/html.py:186 +msgid "previous" msgstr "" -#: sphinx/textwriter.py:422 -msgid "[image]" +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " msgstr "" #: sphinx/directives/desc.py:25 @@ -129,7 +107,7 @@ msgstr "" msgid "%s (built-in variable)" msgstr "" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "" @@ -164,62 +142,66 @@ msgstr "" msgid "%s() (%s static method)" msgstr "" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +msgid "Parameter" +msgstr "" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "" @@ -245,10 +227,20 @@ msgstr "" msgid "Author: " msgstr "" -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "" @@ -345,6 +337,14 @@ msgstr "" msgid "built-in function" msgstr "" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "" @@ -365,17 +365,17 @@ msgstr "" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "" -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -439,27 +439,23 @@ msgstr "" msgid "Previous topic" msgstr "" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "" @@ -467,67 +463,55 @@ msgstr "" msgid "Quick search" msgstr "" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +msgid "Enter search terms or a module, class or function name." msgstr "" -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "" -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "" -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "" -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." msgstr "" -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "" @@ -536,14 +520,13 @@ msgstr "" msgid "Search %(docstitle)s" msgstr "" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -551,11 +534,11 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "" @@ -587,3 +570,16 @@ msgstr "" msgid "Other changes" msgstr "" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "" + diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js new file mode 100644 index 00000000..d1089bf1 --- /dev/null +++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "uk_UA", "plural_expr": "(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)", "messages": {"module, in ": "\u043c\u043e\u0434\u0443\u043b\u044c, \u0432 ", "Preparing search...": "\u041f\u0456\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0434\u043e \u043f\u043e\u0448\u0443\u043a\u0443...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u0412\u0430\u0448 \u043f\u043e\u0448\u0443\u043a \u043d\u0435 \u0432\u0438\u044f\u0432\u0438\u0432 \u0436\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u043f\u0456\u0432\u043f\u0430\u0434\u0456\u043d\u043d\u044f. \u0411\u0443\u0434\u044c-\u043b\u0430\u0441\u043a\u0430 \u043f\u0435\u0440\u0435\u043a\u043e\u043d\u0430\u0439\u0442\u0435\u0441\u044f \u0449\u043e \u0432\u0441\u0456 \u0441\u043b\u043e\u0432\u0430 \u043d\u0430\u0431\u0440\u0430\u043d\u0456 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0456 \u0432\u0438 \u043e\u0431\u0440\u0430\u043b\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043d\u044c\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0456\u0439.", "Search finished, found %s page(s) matching the search query.": "\u041f\u043e\u0448\u0443\u043a \u0437\u0430\u043a\u0456\u043d\u0447\u0435\u043d\u043e, \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e %s \u0441\u0442\u043e\u0440\u0456\u043d\u043e\u043a \u044f\u043a\u0456 \u0441\u043f\u0456\u0432\u043f\u0430\u043b\u0438 \u0437 \u043f\u043e\u0448\u0443\u043a\u043e\u0432\u0438\u043c \u0437\u0430\u043f\u0438\u0442\u043e\u043c.", ", in ": ", \u0432 ", "Permalink to this headline": "\u041f\u043e\u0441\u0442\u0456\u0439\u043d\u0435 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u043d\u0430 \u0446\u0435\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a", "Searching": "\u0428\u0443\u043a\u0430\u044e", "Permalink to this definition": "\u041f\u043e\u0441\u0442\u0456\u0439\u043d\u0435 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u043d\u0430 \u0446\u0435 \u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f", "Hide Search Matches": "\u041f\u0440\u0438\u0445\u043e\u0432\u0430\u0442\u0438 \u0441\u043f\u0456\u0432\u043f\u0430\u0434\u0456\u043d\u043d\u044f \u043f\u043e\u0448\u0443\u043a\u0443", "Search Results": "\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0438 \u043f\u043e\u0448\u0443\u043a\u0443"}});
\ No newline at end of file diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo Binary files differnew file mode 100644 index 00000000..db1ce221 --- /dev/null +++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po new file mode 100644 index 00000000..9f6992f0 --- /dev/null +++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po @@ -0,0 +1,595 @@ +# Ukrainian (Ukraine) translations for Sphinx.
+# Copyright (C) 2009 ORGANIZATION
+# This file is distributed under the same license as the Sphinx project.
+# Petro Sasnyk <petro@sasnyk.name>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 0.6\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-12-28 23:40+0100\n"
+"PO-Revision-Date: 2009-01-14 17:34+0200\n"
+"Last-Translator: Petro Sasnyk <petro@sasnyk.name>\n"
+"Language-Team: uk_UA <LL@li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: sphinx/environment.py:104 sphinx/writers/latex.py:170
+#, python-format
+msgid "%B %d, %Y"
+msgstr ""
+
+#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2
+#: sphinx/templates/genindex-split.html:2
+#: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2
+#: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48
+#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176
+msgid "Index"
+msgstr "Індекс"
+
+#: sphinx/environment.py:301 sphinx/writers/latex.py:175
+msgid "Module Index"
+msgstr "Індекс модулів"
+
+#: sphinx/environment.py:302 sphinx/templates/defindex.html:16
+msgid "Search Page"
+msgstr "Сторінка пошуку"
+
+#: sphinx/roles.py:53 sphinx/directives/desc.py:580
+#, python-format
+msgid "environment variable; %s"
+msgstr "змінна оточення; %s"
+
+#: sphinx/roles.py:60
+#, python-format
+msgid "Python Enhancement Proposals!PEP %s"
+msgstr "Python Enhancement Proposals!PEP %s"
+
+#: sphinx/builders/changes.py:64
+msgid "Builtins"
+msgstr "Вбудовані елементи"
+
+#: sphinx/builders/changes.py:66
+msgid "Module level"
+msgstr "Рівень модуля"
+
+#: sphinx/builders/html.py:118
+#, python-format
+msgid "%b %d, %Y"
+msgstr "%b %d, %Y"
+
+#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21
+msgid "General Index"
+msgstr "Загальний індекс"
+
+#: sphinx/builders/html.py:137
+msgid "index"
+msgstr "індекс"
+
+#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182
+#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19
+#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13
+msgid "Global Module Index"
+msgstr "Загальний індекс модулів"
+
+#: sphinx/builders/html.py:139
+msgid "modules"
+msgstr "модулі"
+
+#: sphinx/builders/html.py:179
+msgid "next"
+msgstr "наступний"
+
+#: sphinx/builders/html.py:186
+msgid "previous"
+msgstr "попередній"
+
+#: sphinx/builders/latex.py:155
+msgid " (in "
+msgstr " (в "
+
+#: sphinx/directives/desc.py:25
+#, python-format
+msgid "%s() (built-in function)"
+msgstr "%s() (вбудована функція)"
+
+#: sphinx/directives/desc.py:26 sphinx/directives/desc.py:42
+#: sphinx/directives/desc.py:54
+#, python-format
+msgid "%s() (in module %s)"
+msgstr "%s() (в модулі %s)"
+
+#: sphinx/directives/desc.py:29
+#, python-format
+msgid "%s (built-in variable)"
+msgstr "%s (вбудована змінна)"
+
+#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78
+#, python-format
+msgid "%s (in module %s)"
+msgstr "%s (в модулі %s)"
+
+#: sphinx/directives/desc.py:33
+#, python-format
+msgid "%s (built-in class)"
+msgstr "%s (вбудований клас)"
+
+#: sphinx/directives/desc.py:34
+#, python-format
+msgid "%s (class in %s)"
+msgstr "%s (клас в %s)"
+
+#: sphinx/directives/desc.py:46
+#, python-format
+msgid "%s() (%s.%s method)"
+msgstr "%s() (%s.%s метод)"
+
+#: sphinx/directives/desc.py:48
+#, python-format
+msgid "%s() (%s method)"
+msgstr "%s() (%s метод)"
+
+#: sphinx/directives/desc.py:58
+#, python-format
+msgid "%s() (%s.%s static method)"
+msgstr "%s() (%s.%s статичний метод)"
+
+#: sphinx/directives/desc.py:60
+#, python-format
+msgid "%s() (%s static method)"
+msgstr "%s() (%s статичний метод)"
+
+#: sphinx/directives/desc.py:82
+#, python-format
+msgid "%s (%s.%s attribute)"
+msgstr "%s (%s.%s атрибут)"
+
+#: sphinx/directives/desc.py:84
+#, python-format
+msgid "%s (%s attribute)"
+msgstr "%s (%s атрибут)"
+
+#: sphinx/directives/desc.py:86
+#, python-format
+msgid "%s (C function)"
+msgstr "%s (С функція)"
+
+#: sphinx/directives/desc.py:88
+#, python-format
+msgid "%s (C member)"
+msgstr "%s (C член)"
+
+#: sphinx/directives/desc.py:90
+#, python-format
+msgid "%s (C macro)"
+msgstr "%s (C макрос)"
+
+#: sphinx/directives/desc.py:92
+#, python-format
+msgid "%s (C type)"
+msgstr "%s (C тип)"
+
+#: sphinx/directives/desc.py:94
+#, python-format
+msgid "%s (C variable)"
+msgstr "%s (C змінна)"
+
+#: sphinx/directives/desc.py:112
+msgid "Raises"
+msgstr "Викликає"
+
+#: sphinx/directives/desc.py:116
+msgid "Variable"
+msgstr "Змінна"
+
+#: sphinx/directives/desc.py:119
+msgid "Returns"
+msgstr "Повертає"
+
+#: sphinx/directives/desc.py:128
+msgid "Return type"
+msgstr "Тип повернення"
+
+#: sphinx/directives/desc.py:213
+msgid "Parameter"
+msgstr "Параметр"
+
+#: sphinx/directives/desc.py:217
+msgid "Parameters"
+msgstr "Параметри"
+
+#: sphinx/directives/desc.py:465
+#, python-format
+msgid "%scommand line option; %s"
+msgstr "%sопція командного рядка; %s"
+
+#: sphinx/directives/other.py:101
+msgid "Platforms: "
+msgstr "Платформи: "
+
+#: sphinx/directives/other.py:106
+#, python-format
+msgid "%s (module)"
+msgstr "%s (модуль)"
+
+#: sphinx/directives/other.py:146
+msgid "Section author: "
+msgstr "Автор секції: "
+
+#: sphinx/directives/other.py:148
+msgid "Module author: "
+msgstr "Автор модуля: "
+
+#: sphinx/directives/other.py:150
+msgid "Author: "
+msgstr "Автор: "
+
+#: sphinx/directives/other.py:249
+msgid "See also"
+msgstr "Дивись також"
+
+#: sphinx/ext/autodoc.py:442
+#, python-format
+msgid " Bases: %s"
+msgstr " Базовий: %s"
+
+#: sphinx/ext/autodoc.py:563 sphinx/ext/autodoc.py:580
+#, python-format
+msgid "alias of :class:`%s`"
+msgstr "синонім :class:`%s`"
+
+#: sphinx/ext/todo.py:31
+msgid "Todo"
+msgstr "Доробити"
+
+#: sphinx/ext/todo.py:75
+#, python-format
+msgid "(The original entry is located in %s, line %d and can be found "
+msgstr "(Початкове входження знаходиться в %s, рядок %d і може бути знайдений "
+
+#: sphinx/ext/todo.py:81
+msgid "here"
+msgstr "тут"
+
+#: sphinx/locale/__init__.py:15
+msgid "Attention"
+msgstr "Увага"
+
+#: sphinx/locale/__init__.py:16
+msgid "Caution"
+msgstr "Застереження"
+
+#: sphinx/locale/__init__.py:17
+msgid "Danger"
+msgstr "Небезпека"
+
+#: sphinx/locale/__init__.py:18
+msgid "Error"
+msgstr "Помилка"
+
+#: sphinx/locale/__init__.py:19
+msgid "Hint"
+msgstr "Підказка"
+
+#: sphinx/locale/__init__.py:20
+msgid "Important"
+msgstr "Важливо"
+
+#: sphinx/locale/__init__.py:21
+msgid "Note"
+msgstr "Примітка"
+
+#: sphinx/locale/__init__.py:22
+msgid "See Also"
+msgstr "Дивись також"
+
+#: sphinx/locale/__init__.py:23
+msgid "Tip"
+msgstr "Порада"
+
+#: sphinx/locale/__init__.py:24
+msgid "Warning"
+msgstr "Попередження"
+
+#: sphinx/locale/__init__.py:28
+#, python-format
+msgid "New in version %s"
+msgstr "Нове в версії %s"
+
+#: sphinx/locale/__init__.py:29
+#, python-format
+msgid "Changed in version %s"
+msgstr "Змінено в версії %s"
+
+#: sphinx/locale/__init__.py:30
+#, python-format
+msgid "Deprecated since version %s"
+msgstr "Застаріло починаючи з версії %s"
+
+#: sphinx/locale/__init__.py:34
+msgid "module"
+msgstr "модуль"
+
+#: sphinx/locale/__init__.py:35
+msgid "keyword"
+msgstr "ключове слово"
+
+#: sphinx/locale/__init__.py:36
+msgid "operator"
+msgstr "оператор"
+
+#: sphinx/locale/__init__.py:37
+msgid "object"
+msgstr "об'єкт"
+
+#: sphinx/locale/__init__.py:38
+msgid "exception"
+msgstr "виняткова ситуація"
+
+#: sphinx/locale/__init__.py:39
+msgid "statement"
+msgstr "вираз"
+
+#: sphinx/locale/__init__.py:40
+msgid "built-in function"
+msgstr "вбудована функція"
+
+#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425
+msgid "Permalink to this headline"
+msgstr "Постійне посилання на цей заголовок"
+
+#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80
+msgid "Permalink to this definition"
+msgstr "Постійне посилання на це визначення"
+
+#: sphinx/static/doctools.js:174
+msgid "Hide Search Matches"
+msgstr "Приховати співпадіння пошуку"
+
+#: sphinx/static/searchtools.js:274
+msgid "Searching"
+msgstr "Шукаю"
+
+#: sphinx/static/searchtools.js:279
+msgid "Preparing search..."
+msgstr "Підготовка до пошуку..."
+
+#: sphinx/static/searchtools.js:338
+msgid "module, in "
+msgstr "модуль, в "
+
+#: sphinx/static/searchtools.js:347
+msgid ", in "
+msgstr ", в "
+
+#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25
+msgid "Search Results"
+msgstr "Результати пошуку"
+
+#: sphinx/static/searchtools.js:455
+msgid ""
+"Your search did not match any documents. Please make sure that all words "
+"are spelled correctly and that you've selected enough categories."
+msgstr ""
+"Ваш пошук не виявив жодного співпадіння. Будь-ласка переконайтеся що всі "
+"слова набрані правильно і ви обрали достатньо категорій."
+
+#: sphinx/static/searchtools.js:457
+#, python-format
+msgid "Search finished, found %s page(s) matching the search query."
+msgstr "Пошук закінчено, знайдено %s сторінок які співпали з пошуковим запитом."
+
+#: sphinx/templates/defindex.html:2
+msgid "Overview"
+msgstr "Огляд"
+
+#: sphinx/templates/defindex.html:11
+msgid "Indices and tables:"
+msgstr "Індекси та таблиці:"
+
+#: sphinx/templates/defindex.html:14
+msgid "Complete Table of Contents"
+msgstr "Повний Зміст"
+
+#: sphinx/templates/defindex.html:15
+msgid "lists all sections and subsections"
+msgstr "перелічити всі секції та підсекції"
+
+#: sphinx/templates/defindex.html:17
+msgid "search this documentation"
+msgstr "шукати цю документацію"
+
+#: sphinx/templates/defindex.html:20
+msgid "quick access to all modules"
+msgstr "швидкий доступ до всіх модулів"
+
+#: sphinx/templates/defindex.html:22
+msgid "all functions, classes, terms"
+msgstr "всі функції, класи, терміни"
+
+#: sphinx/templates/genindex-single.html:5
+#, python-format
+msgid "Index – %(key)s"
+msgstr "Індекс – %(key)"
+
+#: sphinx/templates/genindex-single.html:44
+#: sphinx/templates/genindex-split.html:14
+#: sphinx/templates/genindex-split.html:27 sphinx/templates/genindex.html:54
+msgid "Full index on one page"
+msgstr "Повний індекс на одній сторінці"
+
+#: sphinx/templates/genindex-split.html:7
+msgid "Index pages by letter"
+msgstr "Індексні сторінки по символам"
+
+#: sphinx/templates/genindex-split.html:15
+msgid "can be huge"
+msgstr "може бути величезним"
+
+#: sphinx/templates/layout.html:9
+msgid "Navigation"
+msgstr "Навігація"
+
+#: sphinx/templates/layout.html:40
+msgid "Table Of Contents"
+msgstr "Зміст"
+
+#: sphinx/templates/layout.html:46
+msgid "Previous topic"
+msgstr "Попередній розділ"
+
+#: sphinx/templates/layout.html:48
+msgid "previous chapter"
+msgstr "Попередній розділ"
+
+#: sphinx/templates/layout.html:51
+msgid "Next topic"
+msgstr "Наступна тема"
+
+#: sphinx/templates/layout.html:53
+msgid "next chapter"
+msgstr "наступний розділ"
+
+#: sphinx/templates/layout.html:58
+msgid "This Page"
+msgstr "Ця сторінка"
+
+#: sphinx/templates/layout.html:61
+msgid "Show Source"
+msgstr "Відобразити вихідний текст"
+
+#: sphinx/templates/layout.html:71
+msgid "Quick search"
+msgstr "Швидкий пошук"
+
+#: sphinx/templates/layout.html:74
+msgid "Go"
+msgstr "Вперед"
+
+#: sphinx/templates/layout.html:78
+msgid "Enter search terms or a module, class or function name."
+msgstr "Введіть пошуковий термін, модуль, клас чи назву функції."
+
+#: sphinx/templates/layout.html:115
+#, python-format
+msgid "Search within %(docstitle)s"
+msgstr "Шукати в %(docstitle)s"
+
+#: sphinx/templates/layout.html:124
+msgid "About these documents"
+msgstr "Про ці документи"
+
+#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2
+#: sphinx/templates/search.html:5
+msgid "Search"
+msgstr "Пошук"
+
+#: sphinx/templates/layout.html:129
+msgid "Copyright"
+msgstr "Авторські права"
+
+#: sphinx/templates/layout.html:174
+#, python-format
+msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+
+#: sphinx/templates/layout.html:176
+#, python-format
+msgid "© Copyright %(copyright)s."
+msgstr "© Copyright %(copyright)s."
+
+#: sphinx/templates/layout.html:179
+#, python-format
+msgid "Last updated on %(last_updated)s."
+msgstr "Востаннє оновлено %(last_updated)s."
+
+#: sphinx/templates/layout.html:182
+#, python-format
+msgid ""
+"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
+"%(sphinx_version)s."
+msgstr ""
+"Створено з використанням <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
+"%(sphinx_version)s."
+
+#: sphinx/templates/modindex.html:36
+msgid "Deprecated"
+msgstr "Застарілий"
+
+#: sphinx/templates/opensearch.xml:4
+#, python-format
+msgid "Search %(docstitle)s"
+msgstr "Пошук %(docstitle)s"
+
+#: sphinx/templates/search.html:9
+msgid ""
+"Please activate JavaScript to enable the search\n"
+" functionality."
+msgstr "Будь-ласка вімкніть підтримку JavaScript, щоб ввікнути\n"
+" пошук."
+
+#: sphinx/templates/search.html:14
+msgid ""
+"From here you can search these documents. Enter your search\n"
+" words into the box below and click \"search\". Note that the search\n"
+" function will automatically search for all of the words. Pages\n"
+" containing fewer words won't appear in the result list."
+msgstr ""
+"Звідси ви можете шукати ці документи. Введіть ваші пошукові\n"
+" слова в поле нижче та натисніть \"пошук\". Зауважте що функція\n"
+" пошуку автоматично шукатиме за всіма словами. Сторінки\n"
+" що містять менше слів не з'являться в результуючому списку."
+
+#: sphinx/templates/search.html:21
+msgid "search"
+msgstr "пошук"
+
+#: sphinx/templates/search.html:27
+msgid "Your search did not match any results."
+msgstr "Ваш пошук не виявив жодних співпадінь."
+
+#: sphinx/templates/changes/frameset.html:5
+#: sphinx/templates/changes/versionchanges.html:12
+#, python-format
+msgid "Changes in Version %(version)s — %(docstitle)s"
+msgstr "Зміни в Версії %(version)s — %(docstitle)"
+
+#: sphinx/templates/changes/rstsource.html:5
+#, python-format
+msgid "%(filename)s — %(docstitle)s"
+msgstr "%(filename) — %(docstitle)"
+
+#: sphinx/templates/changes/versionchanges.html:17
+#, python-format
+msgid "Automatically generated list of changes in version %(version)s"
+msgstr "Автоматичного згенерований список змін в версії %(version)"
+
+#: sphinx/templates/changes/versionchanges.html:18
+msgid "Library changes"
+msgstr "Зміни в бібліотеці"
+
+#: sphinx/templates/changes/versionchanges.html:23
+msgid "C API changes"
+msgstr "зміни C API"
+
+#: sphinx/templates/changes/versionchanges.html:25
+msgid "Other changes"
+msgstr "Інші зміни"
+
+#: sphinx/writers/latex.py:173
+msgid "Release"
+msgstr "Реліз"
+
+#: sphinx/writers/text.py:166
+#, python-format
+msgid "Platform: %s"
+msgstr "Платформа: %s"
+
+#: sphinx/writers/text.py:427
+msgid "[image]"
+msgstr ""
+
diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo Binary files differindex 4412f12f..f0576ccd 100644 --- a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po index 2b824a35..9dc3c8ea 100644 --- a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po @@ -8,93 +8,37 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-11-09 19:46+0100\n" -"PO-Revision-Date: 2008-11-27 18:40+0100\n" +"PO-Revision-Date: 2009-01-24 18:39+0000\n" "Last-Translator: Fred Lin <gasolin@gmail.com>\n" "Language-Team: tw <LL@li.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.3\n" -#: sphinx/builder.py:408 -#, python-format -msgid "%b %d, %Y" -msgstr "%Y 年 %m 月 %d 日" - -#: sphinx/builder.py:427 sphinx/templates/defindex.html:21 -msgid "General Index" -msgstr "總索引" - -#: sphinx/builder.py:427 -msgid "index" -msgstr "索引" - -#: sphinx/builder.py:429 sphinx/htmlhelp.py:156 -#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2 -#: sphinx/templates/modindex.html:13 -msgid "Global Module Index" -msgstr "" - -#: sphinx/builder.py:429 -msgid "modules" -msgstr "模組" - -#: sphinx/builder.py:466 -msgid "next" -msgstr "下一頁" - -#: sphinx/builder.py:473 -msgid "previous" -msgstr "上一頁" - -#: sphinx/builder.py:1054 -msgid " (in " -msgstr "" - -#: sphinx/builder.py:1129 -msgid "Builtins" -msgstr "" - -#: sphinx/builder.py:1131 -msgid "Module level" -msgstr "" - -#: sphinx/environment.py:102 sphinx/latexwriter.py:169 +#: sphinx/environment.py:104 sphinx/writers/latex.py:170 #, python-format msgid "%B %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/environment.py:291 sphinx/latexwriter.py:175 -#: sphinx/templates/genindex-single.html:2 +#: sphinx/environment.py:300 sphinx/templates/genindex-single.html:2 #: sphinx/templates/genindex-split.html:2 #: sphinx/templates/genindex-split.html:5 sphinx/templates/genindex.html:2 #: sphinx/templates/genindex.html:5 sphinx/templates/genindex.html:48 -#: sphinx/templates/layout.html:130 +#: sphinx/templates/layout.html:126 sphinx/writers/latex.py:176 msgid "Index" msgstr "索引" -#: sphinx/environment.py:292 sphinx/latexwriter.py:174 +#: sphinx/environment.py:301 sphinx/writers/latex.py:175 msgid "Module Index" msgstr "模組索引" -#: sphinx/environment.py:293 sphinx/templates/defindex.html:16 +#: sphinx/environment.py:302 sphinx/templates/defindex.html:16 msgid "Search Page" msgstr "搜尋頁面" -#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145 -msgid "Permalink to this definition" -msgstr "" - -#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139 -msgid "Permalink to this headline" -msgstr "" - -#: sphinx/latexwriter.py:172 -msgid "Release" -msgstr "釋出" - -#: sphinx/roles.py:53 sphinx/directives/desc.py:537 +#: sphinx/roles.py:53 sphinx/directives/desc.py:580 #, python-format msgid "environment variable; %s" msgstr "環境變數; %s" @@ -104,14 +48,48 @@ msgstr "環境變數; %s" msgid "Python Enhancement Proposals!PEP %s" msgstr "Python 建議文件!PEP %s" -#: sphinx/textwriter.py:166 +#: sphinx/builders/changes.py:64 +msgid "Builtins" +msgstr "" + +#: sphinx/builders/changes.py:66 +msgid "Module level" +msgstr "" + +#: sphinx/builders/html.py:118 #, python-format -msgid "Platform: %s" -msgstr "平台:%s" +msgid "%b %d, %Y" +msgstr "%Y 年 %m 月 %d 日" -#: sphinx/textwriter.py:422 -msgid "[image]" -msgstr "[圖片]" +#: sphinx/builders/html.py:137 sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "總索引" + +#: sphinx/builders/html.py:137 +msgid "index" +msgstr "索引" + +#: sphinx/builders/html.py:139 sphinx/builders/htmlhelp.py:182 +#: sphinx/builders/qthelp.py:131 sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "" + +#: sphinx/builders/html.py:139 +msgid "modules" +msgstr "模組" + +#: sphinx/builders/html.py:179 +msgid "next" +msgstr "下一頁" + +#: sphinx/builders/html.py:186 +msgid "previous" +msgstr "上一頁" + +#: sphinx/builders/latex.py:155 sphinx/builders/pdf.py:162 +msgid " (in " +msgstr "" #: sphinx/directives/desc.py:25 #, python-format @@ -129,7 +107,7 @@ msgstr "%s() (在 %s 模組中)" msgid "%s (built-in variable)" msgstr "%s (內建變數)" -#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66 +#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:78 #, python-format msgid "%s (in module %s)" msgstr "%s() (在 %s 模組中)" @@ -164,62 +142,67 @@ msgstr "%s() (%s.%s 靜態方法)" msgid "%s() (%s static method)" msgstr "%s() (%s 靜態方法)" -#: sphinx/directives/desc.py:70 +#: sphinx/directives/desc.py:82 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s 屬性)" -#: sphinx/directives/desc.py:72 +#: sphinx/directives/desc.py:84 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s 屬性)" -#: sphinx/directives/desc.py:74 +#: sphinx/directives/desc.py:86 #, python-format msgid "%s (C function)" msgstr "%s (C 函式)" -#: sphinx/directives/desc.py:76 +#: sphinx/directives/desc.py:88 #, python-format msgid "%s (C member)" msgstr "%s (C 成員)" -#: sphinx/directives/desc.py:78 +#: sphinx/directives/desc.py:90 #, python-format msgid "%s (C macro)" msgstr "%s (C 巨集)" -#: sphinx/directives/desc.py:80 +#: sphinx/directives/desc.py:92 #, python-format msgid "%s (C type)" msgstr "%s (C 類別)" -#: sphinx/directives/desc.py:82 +#: sphinx/directives/desc.py:94 #, python-format msgid "%s (C variable)" msgstr "%s (C 變數)" -#: sphinx/directives/desc.py:100 +#: sphinx/directives/desc.py:112 msgid "Raises" msgstr "" -#: sphinx/directives/desc.py:104 +#: sphinx/directives/desc.py:116 msgid "Variable" msgstr "變數" -#: sphinx/directives/desc.py:107 +#: sphinx/directives/desc.py:119 msgid "Returns" msgstr "返回" -#: sphinx/directives/desc.py:116 +#: sphinx/directives/desc.py:128 msgid "Return type" msgstr "返回類別" -#: sphinx/directives/desc.py:143 +#: sphinx/directives/desc.py:213 +#, fuzzy +msgid "Parameter" +msgstr "參數" + +#: sphinx/directives/desc.py:217 msgid "Parameters" msgstr "參數" -#: sphinx/directives/desc.py:423 +#: sphinx/directives/desc.py:465 #, python-format msgid "%scommand line option; %s" msgstr "%s命令列選項; %s" @@ -245,10 +228,20 @@ msgstr "模組作者:" msgid "Author: " msgstr "作者:" -#: sphinx/directives/other.py:246 +#: sphinx/directives/other.py:249 msgid "See also" msgstr "" +#: sphinx/ext/autodoc.py:442 +#, python-format +msgid " Bases: %s" +msgstr "" + +#: sphinx/ext/autodoc.py:566 sphinx/ext/autodoc.py:583 +#, python-format +msgid "alias of :class:`%s`" +msgstr "" + #: sphinx/ext/todo.py:31 msgid "Todo" msgstr "待辦" @@ -345,6 +338,14 @@ msgstr "" msgid "built-in function" msgstr "內建函式" +#: sphinx/static/doctools.js:139 sphinx/writers/html.py:425 +msgid "Permalink to this headline" +msgstr "" + +#: sphinx/static/doctools.js:145 sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "" + #: sphinx/static/doctools.js:174 msgid "Hide Search Matches" msgstr "" @@ -365,17 +366,17 @@ msgstr "" msgid ", in " msgstr "" -#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18 +#: sphinx/static/searchtools.js:453 sphinx/templates/search.html:25 msgid "Search Results" msgstr "搜尋結果" -#: sphinx/static/searchtools.js:449 +#: sphinx/static/searchtools.js:455 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "" -#: sphinx/static/searchtools.js:451 +#: sphinx/static/searchtools.js:457 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -439,27 +440,23 @@ msgstr "內容目錄" msgid "Previous topic" msgstr "上一個主題" -#: sphinx/templates/layout.html:47 +#: sphinx/templates/layout.html:48 msgid "previous chapter" msgstr "上一章" -#: sphinx/templates/layout.html:50 +#: sphinx/templates/layout.html:51 msgid "Next topic" msgstr "下一個主題" -#: sphinx/templates/layout.html:51 +#: sphinx/templates/layout.html:53 msgid "next chapter" msgstr "下一章" -#: sphinx/templates/layout.html:55 +#: sphinx/templates/layout.html:58 msgid "This Page" msgstr "本頁" -#: sphinx/templates/layout.html:59 -msgid "Suggest Change" -msgstr "" - -#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62 +#: sphinx/templates/layout.html:61 msgid "Show Source" msgstr "顯示原始碼" @@ -467,67 +464,56 @@ msgstr "顯示原始碼" msgid "Quick search" msgstr "快速搜尋" -#: sphinx/templates/layout.html:71 -msgid "Keyword search" -msgstr "關鍵字搜尋" - -#: sphinx/templates/layout.html:73 +#: sphinx/templates/layout.html:74 msgid "Go" msgstr "" #: sphinx/templates/layout.html:78 -msgid "Enter a module, class or function name." +#, fuzzy +msgid "Enter search terms or a module, class or function name." msgstr "輸入一個模組、類別、或是函式名稱." -#: sphinx/templates/layout.html:119 +#: sphinx/templates/layout.html:115 #, python-format msgid "Search within %(docstitle)s" msgstr "在 %(docstitle)s 中搜尋" -#: sphinx/templates/layout.html:128 +#: sphinx/templates/layout.html:124 msgid "About these documents" msgstr "" -#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2 +#: sphinx/templates/layout.html:127 sphinx/templates/search.html:2 #: sphinx/templates/search.html:5 msgid "Search" msgstr "搜尋" -#: sphinx/templates/layout.html:133 +#: sphinx/templates/layout.html:129 msgid "Copyright" msgstr "版權所有" -#: sphinx/templates/layout.html:178 +#: sphinx/templates/layout.html:174 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "" -#: sphinx/templates/layout.html:180 +#: sphinx/templates/layout.html:176 #, python-format msgid "© Copyright %(copyright)s." msgstr "" -#: sphinx/templates/layout.html:183 +#: sphinx/templates/layout.html:179 #, python-format msgid "Last updated on %(last_updated)s." msgstr "最後更新日期是 %(last_updated)s." -#: sphinx/templates/layout.html:186 +#: sphinx/templates/layout.html:182 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." msgstr "" -#: sphinx/templates/modindex.html:15 -msgid "Most popular modules:" -msgstr "" - -#: sphinx/templates/modindex.html:24 -msgid "Show modules only available on these platforms" -msgstr "" - -#: sphinx/templates/modindex.html:56 +#: sphinx/templates/modindex.html:36 msgid "Deprecated" msgstr "已移除" @@ -536,14 +522,13 @@ msgstr "已移除" msgid "Search %(docstitle)s" msgstr "搜尋 %(docstitle)s" -#: sphinx/templates/page.html:8 +#: sphinx/templates/search.html:9 msgid "" -"<strong>Note:</strong> You requested an out-of-date URL from this server." -" We've tried to redirect you to the new location of this page, but it may" -" not be the right one." +"Please activate JavaScript to enable the search\n" +" functionality." msgstr "" -#: sphinx/templates/search.html:7 +#: sphinx/templates/search.html:14 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -551,11 +536,11 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" -#: sphinx/templates/search.html:14 +#: sphinx/templates/search.html:21 msgid "search" msgstr "搜尋" -#: sphinx/templates/search.html:20 +#: sphinx/templates/search.html:27 msgid "Your search did not match any results." msgstr "" @@ -587,12 +572,16 @@ msgstr "C API 改變" msgid "Other changes" msgstr "其他改變:" -#~ msgid "" -#~ "From here you can search these documents. Enter your search\n" -#~ " words into the box below and" -#~ " click \"search\". Note that the " -#~ "search\n" -#~ " function will automatically search for all of the words. Pages\n" -#~ " containing less words won't appear in the result list." -#~ msgstr "" +#: sphinx/writers/latex.py:173 +msgid "Release" +msgstr "釋出" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "平台:%s" + +#: sphinx/writers/text.py:427 +msgid "[image]" +msgstr "[圖片]" diff --git a/sphinx/pycode/Grammar.txt b/sphinx/pycode/Grammar.txt new file mode 100644 index 00000000..1f4a50ff --- /dev/null +++ b/sphinx/pycode/Grammar.txt @@ -0,0 +1,155 @@ +# Grammar for Python + +# Note: Changing the grammar specified in this file will most likely +# require corresponding changes in the parser module +# (../Modules/parsermodule.c). If you can't make the changes to +# that module yourself, please co-ordinate the required changes +# with someone who can; ask around on python-dev for help. Fred +# Drake <fdrake@acm.org> will probably be listening there. + +# NOTE WELL: You should also follow all the steps listed in PEP 306, +# "How to Change Python's Grammar" + +# Commands for Kees Blom's railroad program +#diagram:token NAME +#diagram:token NUMBER +#diagram:token STRING +#diagram:token NEWLINE +#diagram:token ENDMARKER +#diagram:token INDENT +#diagram:output\input python.bla +#diagram:token DEDENT +#diagram:output\textwidth 20.04cm\oddsidemargin 0.0cm\evensidemargin 0.0cm +#diagram:rules + +# Start symbols for the grammar: +# file_input is a module or sequence of commands read from an input file; +# single_input is a single interactive statement; +# eval_input is the input for the eval() and input() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +file_input: (NEWLINE | stmt)* ENDMARKER +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +eval_input: testlist NEWLINE* ENDMARKER + +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +decorators: decorator+ +decorated: decorators (classdef | funcdef) +funcdef: 'def' NAME parameters ['->' test] ':' suite +parameters: '(' [typedargslist] ')' +typedargslist: ((tfpdef ['=' test] ',')* + ('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname) + | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) +tname: NAME [':' test] +tfpdef: tname | '(' tfplist ')' +tfplist: tfpdef (',' tfpdef)* [','] +varargslist: ((vfpdef ['=' test] ',')* + ('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname) + | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) +vname: NAME +vfpdef: vname | '(' vfplist ')' +vfplist: vfpdef (',' vfpdef)* [','] + +stmt: simple_stmt | compound_stmt +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | exec_stmt | assert_stmt) +expr_stmt: testlist (augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist))*) +augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//=') +# For normal assignments, additional restrictions enforced by the interpreter +print_stmt: 'print' ( [ test (',' test)* [','] ] | + '>>' test [ (',' test)+ [','] ] ) +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist] +yield_stmt: yield_expr +raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]] +import_stmt: import_name | import_from +import_name: 'import' dotted_as_names +import_from: ('from' ('.'* dotted_name | '.'+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)) +import_as_name: NAME ['as' NAME] +dotted_as_name: dotted_name ['as' NAME] +import_as_names: import_as_name (',' import_as_name)* [','] +dotted_as_names: dotted_as_name (',' dotted_as_name)* +dotted_name: NAME ('.' NAME)* +global_stmt: ('global' | 'nonlocal') NAME (',' NAME)* +exec_stmt: 'exec' expr ['in' test [',' test]] +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +while_stmt: 'while' test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ['else' ':' suite] + ['finally' ':' suite] | + 'finally' ':' suite)) +with_stmt: 'with' test [ with_var ] ':' suite +with_var: 'as' expr +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test [(',' | 'as') test]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +# Backward compatibility cruft to support: +# [ x for x in lambda: True, lambda: False if x() ] +# even while also allowing: +# lambda x: 5 if x else 2 +# (But not a mix of the two) +testlist_safe: old_test [(',' old_test)+ [',']] +old_test: or_test | old_lambdef +old_lambdef: 'lambda' [varargslist] ':' old_test + +test: or_test ['if' or_test 'else' test] | lambdef +or_test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom trailer* ['**' factor] +atom: ('(' [yield_expr|testlist_gexp] ')' | + '[' [listmaker] ']' | + '{' [dictsetmaker] '}' | + '`' testlist1 '`' | + NAME | NUMBER | STRING+ | '.' '.' '.') +listmaker: test ( comp_for | (',' test)* [','] ) +testlist_gexp: test ( comp_for | (',' test)* [','] ) +lambdef: 'lambda' [varargslist] ':' test +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: expr (',' expr)* [','] +testlist: test (',' test)* [','] +dictsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | + (test (comp_for | (',' test)* [','])) ) + +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +arglist: (argument ',')* (argument [','] + |'*' test (',' argument)* [',' '**' test] + |'**' test) +argument: test [comp_for] | test '=' test # Really [keyword '='] test + +comp_iter: comp_for | comp_if +comp_for: 'for' exprlist 'in' testlist_safe [comp_iter] +comp_if: 'if' old_test [comp_iter] + +testlist1: test (',' test)* + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME + +yield_expr: 'yield' [testlist] diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py new file mode 100644 index 00000000..de1c3dbe --- /dev/null +++ b/sphinx/pycode/__init__.py @@ -0,0 +1,310 @@ +# -*- coding: utf-8 -*- +""" + sphinx.pycode + ~~~~~~~~~~~~~ + + Utilities parsing and analyzing Python code. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +from os import path +from cStringIO import StringIO + +from sphinx.pycode import nodes +from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals +from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc + + +# load the Python grammar +_grammarfile = path.join(path.dirname(__file__), 'Grammar.txt') +pygrammar = driver.load_grammar(_grammarfile) +pydriver = driver.Driver(pygrammar, convert=nodes.convert) + +# an object with attributes corresponding to token and symbol names +class sym: pass +for k, v in pygrammar.symbol2number.iteritems(): + setattr(sym, k, v) +for k, v in token.tok_name.iteritems(): + setattr(sym, v, k) + +# a dict mapping terminal and nonterminal numbers to their names +number2name = pygrammar.number2symbol.copy() +number2name.update(token.tok_name) + + +# a regex to recognize coding cookies +_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') + +_eq = nodes.Leaf(token.EQUAL, '=') + + +class AttrDocVisitor(nodes.NodeVisitor): + """ + Visitor that collects docstrings for attribute assignments on toplevel and + in classes. + + The docstrings can either be in special '#:' comments before the assignment + or in a docstring after it. + """ + def init(self, scope, encoding): + self.scope = scope + self.encoding = encoding + self.namespace = [] + self.collected = {} + + def visit_classdef(self, node): + self.namespace.append(node[1].value) + self.generic_visit(node) + self.namespace.pop() + + def visit_expr_stmt(self, node): + """Visit an assignment which may have a special comment before it.""" + if _eq not in node.children: + # not an assignment (we don't care for augmented assignments) + return + pnode = node[0] + prefix = pnode.get_prefix() + # if the assignment is the first statement on a new indentation + # level, its preceding whitespace and comments are not assigned + # to that token, but the first INDENT or DEDENT token + while not prefix: + pnode = pnode.get_prev_leaf() + if not pnode or pnode.type not in (token.INDENT, token.DEDENT): + break + prefix = pnode.get_prefix() + prefix = prefix.decode(self.encoding) + docstring = prepare_commentdoc(prefix) + if docstring: + self.add_docstring(node, docstring) + + def visit_simple_stmt(self, node): + """Visit a docstring statement which may have an assignment before.""" + if node[0].type != token.STRING: + # not a docstring; but still need to visit children + return self.generic_visit(node) + prev = node.get_prev_sibling() + if not prev: + return + if prev.type == sym.simple_stmt and \ + prev[0].type == sym.expr_stmt and _eq in prev[0].children: + # need to "eval" the string because it's returned in its + # original form + docstring = literals.evalString(node[0].value, self.encoding) + docstring = prepare_docstring(docstring) + self.add_docstring(prev[0], docstring) + + def visit_funcdef(self, node): + # don't descend into functions -- nothing interesting there + return + + def add_docstring(self, node, docstring): + # add an item for each assignment target + for i in range(0, len(node) - 1, 2): + target = node[i] + if target.type != token.NAME: + # don't care about complex targets + continue + namespace = '.'.join(self.namespace) + if namespace.startswith(self.scope): + self.collected[namespace, target.value] = docstring + + +class PycodeError(Exception): + def __str__(self): + res = self.args[0] + if len(self.args) > 1: + res += ' (exception was: %r)' % self.args[1] + return res + + +class ModuleAnalyzer(object): + # cache for analyzer objects -- caches both by module and file name + cache = {} + + @classmethod + def for_string(cls, string, modname, srcname='<string>'): + return cls(StringIO(string), modname, srcname) + + @classmethod + def for_file(cls, filename, modname): + if ('file', filename) in cls.cache: + return cls.cache['file', filename] + try: + fileobj = open(filename, 'r') + except Exception, err: + raise PycodeError('error opening %r' % filename, err) + obj = cls(fileobj, modname, filename) + cls.cache['file', filename] = obj + return obj + + @classmethod + def for_module(cls, modname): + if ('module', modname) in cls.cache: + entry = cls.cache['module', modname] + if isinstance(entry, PycodeError): + raise entry + return entry + + try: + if modname not in sys.modules: + try: + __import__(modname) + except ImportError, err: + raise PycodeError('error importing %r' % modname, err) + mod = sys.modules[modname] + if hasattr(mod, '__loader__'): + try: + source = mod.__loader__.get_source(modname) + except Exception, err: + raise PycodeError('error getting source for %r' % modname, + err) + obj = cls.for_string(source, modname) + cls.cache['module', modname] = obj + return obj + filename = getattr(mod, '__file__', None) + if filename is None: + raise PycodeError('no source found for module %r' % modname) + filename = path.normpath(path.abspath(filename)) + lfilename = filename.lower() + if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'): + filename = filename[:-1] + elif not lfilename.endswith('.py'): + raise PycodeError('source is not a .py file: %r' % filename) + if not path.isfile(filename): + raise PycodeError('source file is not present: %r' % filename) + obj = cls.for_file(filename, modname) + except PycodeError, err: + cls.cache['module', modname] = err + raise + cls.cache['module', modname] = obj + return obj + + def __init__(self, source, modname, srcname): + # name of the module + self.modname = modname + # name of the source file + self.srcname = srcname + # file-like object yielding source lines + self.source = source + + # will be filled by tokenize() + self.tokens = None + # will be filled by parse() + self.parsetree = None + # will be filled by find_attr_docs() + self.attr_docs = None + # will be filled by find_tags() + self.tags = None + + def tokenize(self): + """Generate tokens from the source.""" + if self.tokens is not None: + return + self.tokens = list(tokenize.generate_tokens(self.source.readline)) + self.source.close() + + def parse(self): + """Parse the generated source tokens.""" + if self.parsetree is not None: + return + self.tokenize() + try: + self.parsetree = pydriver.parse_tokens(self.tokens) + except parse.ParseError, err: + raise PycodeError('parsing failed', err) + # find the source code encoding + encoding = sys.getdefaultencoding() + comments = self.parsetree.get_prefix() + for line in comments.splitlines()[:2]: + match = _coding_re.search(line) + if match is not None: + encoding = match.group(1) + break + self.encoding = encoding + + def find_attr_docs(self, scope=''): + """Find class and module-level attributes and their documentation.""" + if self.attr_docs is not None: + return self.attr_docs + self.parse() + attr_visitor = AttrDocVisitor(number2name, scope, self.encoding) + attr_visitor.visit(self.parsetree) + self.attr_docs = attr_visitor.collected + return attr_visitor.collected + + def find_tags(self): + """Find class, function and method definitions and their location.""" + if self.tags is not None: + return self.tags + self.tokenize() + result = {} + namespace = [] + stack = [] + indent = 0 + defline = False + expect_indent = False + def tokeniter(ignore = (token.COMMENT, token.NL)): + for tokentup in self.tokens: + if tokentup[0] not in ignore: + yield tokentup + tokeniter = tokeniter() + for type, tok, spos, epos, line in tokeniter: + if expect_indent: + if type != token.INDENT: + # no suite -- one-line definition + assert stack + dtype, fullname, startline, _ = stack.pop() + endline = epos[0] + namespace.pop() + result[fullname] = (dtype, startline, endline) + expect_indent = False + if tok in ('def', 'class'): + name = tokeniter.next()[1] + namespace.append(name) + fullname = '.'.join(namespace) + stack.append((tok, fullname, spos[0], indent)) + defline = True + elif type == token.INDENT: + expect_indent = False + indent += 1 + elif type == token.DEDENT: + indent -= 1 + # if the stacklevel is the same as it was before the last + # def/class block, this dedent closes that block + if stack and indent == stack[-1][3]: + dtype, fullname, startline, _ = stack.pop() + endline = spos[0] + namespace.pop() + result[fullname] = (dtype, startline, endline) + elif type == token.NEWLINE: + # if this line contained a definition, expect an INDENT + # to start the suite; if there is no such INDENT + # it's a one-line definition + if defline: + defline = False + expect_indent = True + self.tags = result + return result + + +if __name__ == '__main__': + import time, pprint + x0 = time.time() + #ma = ModuleAnalyzer.for_file(__file__.rstrip('c'), 'sphinx.builders.html') + ma = ModuleAnalyzer.for_file('sphinx/builders/html.py', + 'sphinx.builders.html') + ma.tokenize() + x1 = time.time() + ma.parse() + x2 = time.time() + #for (ns, name), doc in ma.find_attr_docs().iteritems(): + # print '>>', ns, name + # print '\n'.join(doc) + pprint.pprint(ma.find_tags()) + x3 = time.time() + #print nodes.nice_repr(ma.parsetree, number2name) + print "tokenizing %.4f, parsing %.4f, finding %.4f" % (x1-x0, x2-x1, x3-x2) diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py new file mode 100644 index 00000000..f8f57740 --- /dev/null +++ b/sphinx/pycode/nodes.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +""" + sphinx.pycode.nodes + ~~~~~~~~~~~~~~~~~~~ + + Parse tree node implementations. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + + +class BaseNode(object): + """ + Node superclass for both terminal and nonterminal nodes. + """ + parent = None + + def _eq(self, other): + raise NotImplementedError + + def __eq__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented + return self._eq(other) + + def __ne__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented + return not self._eq(other) + + def get_prev_sibling(self): + """Return previous child in parent's children, or None.""" + if self.parent is None: + return None + for i, child in enumerate(self.parent.children): + if child is self: + if i == 0: + return None + return self.parent.children[i-1] + + def get_next_sibling(self): + """Return next child in parent's children, or None.""" + if self.parent is None: + return None + for i, child in enumerate(self.parent.children): + if child is self: + try: + return self.parent.children[i+1] + except IndexError: + return None + + def get_prev_leaf(self): + """Return the leaf node that precedes this node in the parse tree.""" + def last_child(node): + if isinstance(node, Leaf): + return node + elif not node.children: + return None + else: + return last_child(node.children[-1]) + if self.parent is None: + return None + prev = self.get_prev_sibling() + if isinstance(prev, Leaf): + return prev + elif prev is not None: + return last_child(prev) + return self.parent.get_prev_leaf() + + def get_next_leaf(self): + """Return self if leaf, otherwise the leaf node that succeeds this + node in the parse tree. + """ + node = self + while not isinstance(node, Leaf): + assert node.children + node = node.children[0] + return node + + def get_lineno(self): + """Return the line number which generated the invocant node.""" + return self.get_next_leaf().lineno + + def get_prefix(self): + """Return the prefix of the next leaf node.""" + # only leaves carry a prefix + return self.get_next_leaf().prefix + + +class Node(BaseNode): + """ + Node implementation for nonterminals. + """ + + def __init__(self, type, children, context=None): + # type of nonterminals is >= 256 + # assert type >= 256, type + self.type = type + self.children = list(children) + for ch in self.children: + # assert ch.parent is None, repr(ch) + ch.parent = self + + def __repr__(self): + return '%s(%s, %r)' % (self.__class__.__name__, + self.type, self.children) + + def __str__(self): + """This reproduces the input source exactly.""" + return ''.join(map(str, self.children)) + + def _eq(self, other): + return (self.type, self.children) == (other.type, other.children) + + # support indexing the node directly instead of .children + + def __getitem__(self, index): + return self.children[index] + + def __iter__(self): + return iter(self.children) + + def __len__(self): + return len(self.children) + + +class Leaf(BaseNode): + """ + Node implementation for leaf nodes (terminals). + """ + prefix = '' # Whitespace and comments preceding this token in the input + lineno = 0 # Line where this token starts in the input + column = 0 # Column where this token tarts in the input + + def __init__(self, type, value, context=None): + # type of terminals is below 256 + # assert 0 <= type < 256, type + self.type = type + self.value = value + if context is not None: + self.prefix, (self.lineno, self.column) = context + + def __repr__(self): + return '%s(%r, %r, %r)' % (self.__class__.__name__, + self.type, self.value, self.prefix) + + def __str__(self): + """This reproduces the input source exactly.""" + return self.prefix + str(self.value) + + def _eq(self, other): + """Compares two nodes for equality.""" + return (self.type, self.value) == (other.type, other.value) + + +def convert(grammar, raw_node): + """Convert raw node to a Node or Leaf instance.""" + type, value, context, children = raw_node + if children or type in grammar.number2symbol: + # If there's exactly one child, return that child instead of + # creating a new node. + if len(children) == 1: + return children[0] + return Node(type, children, context=context) + else: + return Leaf(type, value, context=context) + + +def nice_repr(node, number2name, prefix=False): + def _repr(node): + if isinstance(node, Leaf): + return "%s(%r)" % (number2name[node.type], node.value) + else: + return "%s(%s)" % (number2name[node.type], + ', '.join(map(_repr, node.children))) + def _prepr(node): + if isinstance(node, Leaf): + return "%s(%r, %r)" % (number2name[node.type], + node.prefix, node.value) + else: + return "%s(%s)" % (number2name[node.type], + ', '.join(map(_prepr, node.children))) + return (prefix and _prepr or _repr)(node) + + +class NodeVisitor(object): + def __init__(self, number2name, *args): + self.number2name = number2name + self.init(*args) + + def init(self, *args): + pass + + def visit(self, node): + """Visit a node.""" + method = 'visit_' + self.number2name[node.type] + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + """Called if no explicit visitor function exists for a node.""" + if isinstance(node, Node): + for child in node: + self.visit(child) diff --git a/sphinx/pycode/pgen2/__init__.py b/sphinx/pycode/pgen2/__init__.py new file mode 100644 index 00000000..af390484 --- /dev/null +++ b/sphinx/pycode/pgen2/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""The pgen2 package.""" diff --git a/sphinx/pycode/pgen2/driver.py b/sphinx/pycode/pgen2/driver.py new file mode 100644 index 00000000..edc882fa --- /dev/null +++ b/sphinx/pycode/pgen2/driver.py @@ -0,0 +1,145 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Modifications: +# Copyright 2006 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Parser driver. + +This provides a high-level interface to parse a file into a syntax tree. + +""" + +__author__ = "Guido van Rossum <guido@python.org>" + +__all__ = ["Driver", "load_grammar"] + +# Python imports +import os +import logging +import sys + +# Pgen imports +from sphinx.pycode.pgen2 import grammar, parse, token, tokenize, pgen + + +class Driver(object): + + def __init__(self, grammar, convert=None, logger=None): + self.grammar = grammar + if logger is None: + logger = logging.getLogger() + self.logger = logger + self.convert = convert + + def parse_tokens(self, tokens, debug=False): + """Parse a series of tokens and return the syntax tree.""" + # XXX Move the prefix computation into a wrapper around tokenize. + p = parse.Parser(self.grammar, self.convert) + p.setup() + lineno = 1 + column = 0 + type = value = start = end = line_text = None + prefix = "" + opmap = grammar.opmap + for type, value, start, end, line_text in tokens: + if start != (lineno, column): + assert (lineno, column) <= start, ((lineno, column), start) + s_lineno, s_column = start + if lineno < s_lineno: + prefix += "\n" * (s_lineno - lineno) + lineno = s_lineno + column = 0 + if column < s_column: + prefix += line_text[column:s_column] + column = s_column + if type in (tokenize.COMMENT, tokenize.NL): + prefix += value + lineno, column = end + if value.endswith("\n"): + lineno += 1 + column = 0 + continue + if type == token.OP: + type = opmap[value] + # if debug: + # self.logger.debug("%s %r (prefix=%r)", + # token.tok_name[type], value, prefix) + if p.addtoken(type, value, (prefix, start)): + # if debug: + # self.logger.debug("Stop.") + break + prefix = "" + lineno, column = end + if value.endswith("\n"): + lineno += 1 + column = 0 + else: + # We never broke out -- EOF is too soon (how can this happen???) + raise parse.ParseError("incomplete input", type, value, line_text) + return p.rootnode + + def parse_stream_raw(self, stream, debug=False): + """Parse a stream and return the syntax tree.""" + tokens = tokenize.generate_tokens(stream.readline) + return self.parse_tokens(tokens, debug) + + def parse_stream(self, stream, debug=False): + """Parse a stream and return the syntax tree.""" + return self.parse_stream_raw(stream, debug) + + def parse_file(self, filename, debug=False): + """Parse a file and return the syntax tree.""" + stream = open(filename) + try: + return self.parse_stream(stream, debug) + finally: + stream.close() + + def parse_string(self, text, debug=False): + """Parse a string and return the syntax tree.""" + tokens = tokenize.generate_tokens(generate_lines(text).next) + return self.parse_tokens(tokens, debug) + + +def generate_lines(text): + """Generator that behaves like readline without using StringIO.""" + for line in text.splitlines(True): + yield line + while True: + yield "" + + +def load_grammar(gt="Grammar.txt", gp=None, + save=True, force=False, logger=None): + """Load the grammar (maybe from a pickle).""" + if logger is None: + logger = logging.getLogger() + if gp is None: + head, tail = os.path.splitext(gt) + if tail == ".txt": + tail = "" + gp = head + tail + ".".join(map(str, sys.version_info)) + ".pickle" + if force or not _newer(gp, gt): + logger.info("Generating grammar tables from %s", gt) + g = pgen.generate_grammar(gt) + if save: + logger.info("Writing grammar tables to %s", gp) + try: + g.dump(gp) + except IOError, e: + logger.info("Writing failed:"+str(e)) + else: + g = grammar.Grammar() + g.load(gp) + return g + + +def _newer(a, b): + """Inquire whether file a was written since file b.""" + if not os.path.exists(a): + return False + if not os.path.exists(b): + return True + return os.path.getmtime(a) >= os.path.getmtime(b) diff --git a/sphinx/pycode/pgen2/grammar.py b/sphinx/pycode/pgen2/grammar.py new file mode 100644 index 00000000..381d80e8 --- /dev/null +++ b/sphinx/pycode/pgen2/grammar.py @@ -0,0 +1,171 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""This module defines the data structures used to represent a grammar. + +These are a bit arcane because they are derived from the data +structures used by Python's 'pgen' parser generator. + +There's also a table here mapping operators to their names in the +token module; the Python tokenize module reports all operators as the +fallback token code OP, but the parser needs the actual token code. + +""" + +# Python imports +import pickle + +# Local imports +from sphinx.pycode.pgen2 import token, tokenize + + +class Grammar(object): + """Pgen parsing tables tables conversion class. + + Once initialized, this class supplies the grammar tables for the + parsing engine implemented by parse.py. The parsing engine + accesses the instance variables directly. The class here does not + provide initialization of the tables; several subclasses exist to + do this (see the conv and pgen modules). + + The load() method reads the tables from a pickle file, which is + much faster than the other ways offered by subclasses. The pickle + file is written by calling dump() (after loading the grammar + tables using a subclass). The report() method prints a readable + representation of the tables to stdout, for debugging. + + The instance variables are as follows: + + symbol2number -- a dict mapping symbol names to numbers. Symbol + numbers are always 256 or higher, to distinguish + them from token numbers, which are between 0 and + 255 (inclusive). + + number2symbol -- a dict mapping numbers to symbol names; + these two are each other's inverse. + + states -- a list of DFAs, where each DFA is a list of + states, each state is is a list of arcs, and each + arc is a (i, j) pair where i is a label and j is + a state number. The DFA number is the index into + this list. (This name is slightly confusing.) + Final states are represented by a special arc of + the form (0, j) where j is its own state number. + + dfas -- a dict mapping symbol numbers to (DFA, first) + pairs, where DFA is an item from the states list + above, and first is a set of tokens that can + begin this grammar rule (represented by a dict + whose values are always 1). + + labels -- a list of (x, y) pairs where x is either a token + number or a symbol number, and y is either None + or a string; the strings are keywords. The label + number is the index in this list; label numbers + are used to mark state transitions (arcs) in the + DFAs. + + start -- the number of the grammar's start symbol. + + keywords -- a dict mapping keyword strings to arc labels. + + tokens -- a dict mapping token numbers to arc labels. + + """ + + def __init__(self): + self.symbol2number = {} + self.number2symbol = {} + self.states = [] + self.dfas = {} + self.labels = [(0, "EMPTY")] + self.keywords = {} + self.tokens = {} + self.symbol2label = {} + self.start = 256 + + def dump(self, filename): + """Dump the grammar tables to a pickle file.""" + f = open(filename, "wb") + pickle.dump(self.__dict__, f, 2) + f.close() + + def load(self, filename): + """Load the grammar tables from a pickle file.""" + f = open(filename, "rb") + d = pickle.load(f) + f.close() + self.__dict__.update(d) + + def report(self): + """Dump the grammar tables to standard output, for debugging.""" + from pprint import pprint + print "s2n" + pprint(self.symbol2number) + print "n2s" + pprint(self.number2symbol) + print "states" + pprint(self.states) + print "dfas" + pprint(self.dfas) + print "labels" + pprint(self.labels) + print "start", self.start + + +# Map from operator to number (since tokenize doesn't do this) + +opmap_raw = """ +( LPAR +) RPAR +[ LSQB +] RSQB +: COLON +, COMMA +; SEMI ++ PLUS +- MINUS +* STAR +/ SLASH +| VBAR +& AMPER +< LESS +> GREATER += EQUAL +. DOT +% PERCENT +` BACKQUOTE +{ LBRACE +} RBRACE +@ AT +== EQEQUAL +!= NOTEQUAL +<> NOTEQUAL +<= LESSEQUAL +>= GREATEREQUAL +~ TILDE +^ CIRCUMFLEX +<< LEFTSHIFT +>> RIGHTSHIFT +** DOUBLESTAR ++= PLUSEQUAL +-= MINEQUAL +*= STAREQUAL +/= SLASHEQUAL +%= PERCENTEQUAL +&= AMPEREQUAL +|= VBAREQUAL +^= CIRCUMFLEXEQUAL +<<= LEFTSHIFTEQUAL +>>= RIGHTSHIFTEQUAL +**= DOUBLESTAREQUAL +// DOUBLESLASH +//= DOUBLESLASHEQUAL +-> RARROW +""" + +opmap = {} +for line in opmap_raw.splitlines(): + if line: + op, name = line.split() + opmap[op] = getattr(token, name) diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py new file mode 100644 index 00000000..31900291 --- /dev/null +++ b/sphinx/pycode/pgen2/literals.py @@ -0,0 +1,96 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Extended to handle raw and unicode literals by Georg Brandl. + +"""Safely evaluate Python string literals without using eval().""" + +import re + +simple_escapes = {"a": "\a", + "b": "\b", + "f": "\f", + "n": "\n", + "r": "\r", + "t": "\t", + "v": "\v", + "'": "'", + '"': '"', + "\\": "\\"} + +def convert_hex(x, n): + if len(x) < n+1: + raise ValueError("invalid hex string escape ('\\%s')" % x) + try: + return int(x[1:], 16) + except ValueError: + raise ValueError("invalid hex string escape ('\\%s')" % x) + +def escape(m): + all, tail = m.group(0, 1) + assert all.startswith("\\") + esc = simple_escapes.get(tail) + if esc is not None: + return esc + elif tail.startswith("x"): + return chr(convert_hex(tail, 2)) + elif tail.startswith('u'): + return unichr(convert_hex(tail, 4)) + elif tail.startswith('U'): + return unichr(convert_hex(tail, 8)) + elif tail.startswith('N'): + import unicodedata + try: + return unicodedata.lookup(tail[1:-1]) + except KeyError: + raise ValueError("undefined character name %r" % tail[1:-1]) + else: + try: + return chr(int(tail, 8)) + except ValueError: + raise ValueError("invalid octal string escape ('\\%s')" % tail) + +def escaperaw(m): + all, tail = m.group(0, 1) + if tail.startswith('u'): + return unichr(convert_hex(tail, 4)) + elif tail.startswith('U'): + return unichr(convert_hex(tail, 8)) + else: + return all + +escape_re = re.compile(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3})") +uni_escape_re = re.compile(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3}|" + r"u[0-9a-fA-F]{0,4}|U[0-9a-fA-F]{0,8}|N\{.+?\})") + +def evalString(s, encoding=None): + regex = escape_re + repl = escape + if encoding: + s = s.decode(encoding) + if s.startswith('u') or s.startswith('U'): + regex = uni_escape_re + s = s[1:] + if s.startswith('r') or s.startswith('R'): + repl = escaperaw + s = s[1:] + assert s.startswith("'") or s.startswith('"'), repr(s[:1]) + q = s[0] + if s[:3] == q*3: + q = q*3 + assert s.endswith(q), repr(s[-len(q):]) + assert len(s) >= 2*len(q) + s = s[len(q):-len(q)] + return regex.sub(repl, s) + +def test(): + for i in range(256): + c = chr(i) + s = repr(c) + e = evalString(s) + if e != c: + print i, c, s, e + + +if __name__ == "__main__": + test() diff --git a/sphinx/pycode/pgen2/parse.c b/sphinx/pycode/pgen2/parse.c new file mode 100644 index 00000000..fd0e9ff9 --- /dev/null +++ b/sphinx/pycode/pgen2/parse.c @@ -0,0 +1,3261 @@ +/* Generated by Cython 0.9.8.1 on Thu Jan 1 23:45:38 2009 */ + +#define PY_SSIZE_T_CLEAN +#include "Python.h" +#include "structmember.h" +#ifndef PY_LONG_LONG + #define PY_LONG_LONG LONG_LONG +#endif +#ifndef DL_EXPORT + #define DL_EXPORT(t) t +#endif +#if PY_VERSION_HEX < 0x02040000 + #define METH_COEXIST 0 +#endif +#if PY_VERSION_HEX < 0x02050000 + typedef int Py_ssize_t; + #define PY_SSIZE_T_MAX INT_MAX + #define PY_SSIZE_T_MIN INT_MIN + #define PyInt_FromSsize_t(z) PyInt_FromLong(z) + #define PyInt_AsSsize_t(o) PyInt_AsLong(o) + #define PyNumber_Index(o) PyNumber_Int(o) + #define PyIndex_Check(o) PyNumber_Check(o) +#endif +#if PY_VERSION_HEX < 0x02060000 + #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) + #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) + #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) + #define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, + #define PyType_Modified(t) + + typedef struct { + void *buf; + Py_ssize_t len; + int readonly; + const char *format; + int ndim; + Py_ssize_t *shape; + Py_ssize_t *strides; + Py_ssize_t *suboffsets; + Py_ssize_t itemsize; + void *internal; + } Py_buffer; + + #define PyBUF_SIMPLE 0 + #define PyBUF_WRITABLE 0x0001 + #define PyBUF_LOCK 0x0002 + #define PyBUF_FORMAT 0x0004 + #define PyBUF_ND 0x0008 + #define PyBUF_STRIDES (0x0010 | PyBUF_ND) + #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) + #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) + #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) + #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) + +#endif +#if PY_MAJOR_VERSION < 3 + #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" +#else + #define __Pyx_BUILTIN_MODULE_NAME "builtins" +#endif +#if PY_MAJOR_VERSION >= 3 + #define Py_TPFLAGS_CHECKTYPES 0 + #define Py_TPFLAGS_HAVE_INDEX 0 +#endif +#if PY_MAJOR_VERSION >= 3 + #define PyBaseString_Type PyUnicode_Type + #define PyString_Type PyBytes_Type + #define PyInt_Type PyLong_Type + #define PyInt_Check(op) PyLong_Check(op) + #define PyInt_CheckExact(op) PyLong_CheckExact(op) + #define PyInt_FromString PyLong_FromString + #define PyInt_FromUnicode PyLong_FromUnicode + #define PyInt_FromLong PyLong_FromLong + #define PyInt_FromSize_t PyLong_FromSize_t + #define PyInt_FromSsize_t PyLong_FromSsize_t + #define PyInt_AsLong PyLong_AsLong + #define PyInt_AS_LONG PyLong_AS_LONG + #define PyInt_AsSsize_t PyLong_AsSsize_t + #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask + #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask + #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) +#else + #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) + #define PyBytes_Type PyString_Type +#endif +#if PY_MAJOR_VERSION >= 3 + #define PyMethod_New(func, self, klass) PyInstanceMethod_New(func) +#endif +#if !defined(WIN32) && !defined(MS_WINDOWS) + #ifndef __stdcall + #define __stdcall + #endif + #ifndef __cdecl + #define __cdecl + #endif +#else + #define _USE_MATH_DEFINES +#endif +#ifdef __cplusplus +#define __PYX_EXTERN_C extern "C" +#else +#define __PYX_EXTERN_C extern +#endif +#include <math.h> +#define __PYX_HAVE_API__sphinx__pycode__pgen2__parse + + +#ifdef __GNUC__ +#define INLINE __inline__ +#elif _WIN32 +#define INLINE __inline +#else +#define INLINE +#endif + +typedef struct {PyObject **p; char *s; long n; char is_unicode; char intern; char is_identifier;} __Pyx_StringTabEntry; /*proto*/ + + + +static int __pyx_skip_dispatch = 0; + + +/* Type Conversion Predeclarations */ + +#if PY_MAJOR_VERSION < 3 +#define __Pyx_PyBytes_FromString PyString_FromString +#define __Pyx_PyBytes_AsString PyString_AsString +#else +#define __Pyx_PyBytes_FromString PyBytes_FromString +#define __Pyx_PyBytes_AsString PyBytes_AsString +#endif + +#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) +static INLINE int __Pyx_PyObject_IsTrue(PyObject* x); +static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x); +static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x); +static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b); + +#define __pyx_PyInt_AsLong(x) (PyInt_CheckExact(x) ? PyInt_AS_LONG(x) : PyInt_AsLong(x)) +#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) + +static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x); +static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x); +static INLINE char __pyx_PyInt_char(PyObject* x); +static INLINE short __pyx_PyInt_short(PyObject* x); +static INLINE int __pyx_PyInt_int(PyObject* x); +static INLINE long __pyx_PyInt_long(PyObject* x); +static INLINE signed char __pyx_PyInt_signed_char(PyObject* x); +static INLINE signed short __pyx_PyInt_signed_short(PyObject* x); +static INLINE signed int __pyx_PyInt_signed_int(PyObject* x); +static INLINE signed long __pyx_PyInt_signed_long(PyObject* x); +static INLINE long double __pyx_PyInt_long_double(PyObject* x); +#ifdef __GNUC__ +/* Test for GCC > 2.95 */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else /* __GNUC__ > 2 ... */ +#define likely(x) (x) +#define unlikely(x) (x) +#endif /* __GNUC__ > 2 ... */ +#else /* __GNUC__ */ +#define likely(x) (x) +#define unlikely(x) (x) +#endif /* __GNUC__ */ + +static PyObject *__pyx_m; +static PyObject *__pyx_b; +static PyObject *__pyx_empty_tuple; +static int __pyx_lineno; +static int __pyx_clineno = 0; +static const char * __pyx_cfilenm= __FILE__; +static const char *__pyx_filename; +static const char **__pyx_f; + +static INLINE void __Pyx_RaiseArgtupleTooLong(Py_ssize_t num_expected, Py_ssize_t num_found); /*proto*/ + +static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/ + +static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/ + +static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/ + +static INLINE PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i, int is_unsigned) { + PyObject *r; + if (PyList_CheckExact(o) && 0 <= i && i < PyList_GET_SIZE(o)) { + r = PyList_GET_ITEM(o, i); + Py_INCREF(r); + } + else if (PyTuple_CheckExact(o) && 0 <= i && i < PyTuple_GET_SIZE(o)) { + r = PyTuple_GET_ITEM(o, i); + Py_INCREF(r); + } + else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_item && (likely(i >= 0) || !is_unsigned)) + r = PySequence_GetItem(o, i); + else { + PyObject *j = (likely(i >= 0) || !is_unsigned) ? PyInt_FromLong(i) : PyLong_FromUnsignedLongLong((sizeof(unsigned long long) > sizeof(Py_ssize_t) ? (1ULL << (sizeof(Py_ssize_t)*8)) : 0) + i); + if (!j) + return 0; + r = PyObject_GetItem(o, j); + Py_DECREF(j); + } + return r; +} + +static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/ +static int __Pyx_EndUnpack(PyObject *); /*proto*/ + +static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ + +static INLINE PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) { + if (likely(PyList_CheckExact(L))) { + if (PyList_Append(L, x) < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; // this is just to have an accurate signature + } + else { + return PyObject_CallMethod(L, "append", "(O)", x); + } +} + +static INLINE int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v, int is_unsigned) { + int r; + if (PyList_CheckExact(o) && 0 <= i && i < PyList_GET_SIZE(o)) { + Py_DECREF(PyList_GET_ITEM(o, i)); + Py_INCREF(v); + PyList_SET_ITEM(o, i, v); + return 1; + } + else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_ass_item && (likely(i >= 0) || !is_unsigned)) + r = PySequence_SetItem(o, i, v); + else { + PyObject *j = (likely(i >= 0) || !is_unsigned) ? PyInt_FromLong(i) : PyLong_FromUnsignedLongLong((sizeof(unsigned long long) > sizeof(Py_ssize_t) ? (1ULL << (sizeof(Py_ssize_t)*8)) : 0) + i); + if (!j) + return -1; + r = PyObject_SetItem(o, j, v); + Py_DECREF(j); + } + return r; +} + +static void __Pyx_WriteUnraisable(const char *name); /*proto*/ + +static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/ + +static void __Pyx_AddTraceback(const char *funcname); /*proto*/ + +static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ + +/* Type declarations */ + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":31 + * + * + * cdef class Parser: # <<<<<<<<<<<<<< + * cdef public grammar, stack, rootnode, used_names + * cdef _grammar_dfas, _grammar_labels, _grammar_keywords, _grammar_tokens + */ + +struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser { + PyObject_HEAD + struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_vtab; + PyObject *grammar; + PyObject *stack; + PyObject *rootnode; + PyObject *used_names; + PyObject *_grammar_dfas; + PyObject *_grammar_labels; + PyObject *_grammar_keywords; + PyObject *_grammar_tokens; + PyObject *_grammar_number2symbol; +}; + + +struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser { + int (*classify)(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *, PyObject *, PyObject *, PyObject *); + void (*shift)(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *, PyObject *, PyObject *, PyObject *, PyObject *); + void (*push)(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *, PyObject *, PyObject *, PyObject *, PyObject *); + void (*pop)(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *); + PyObject *(*convert)(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *, PyObject *); +}; +static struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_vtabptr_6sphinx_6pycode_5pgen2_5parse_Parser; +/* Module declarations from sphinx.pycode.pgen2.parse */ + +static PyTypeObject *__pyx_ptype_6sphinx_6pycode_5pgen2_5parse_Parser = 0; + + +/* Implementation of sphinx.pycode.pgen2.parse */ +static char __pyx_k_2[] = "Exception to signal the parser is stuck."; +static PyObject *__pyx_int_0; +static PyObject *__pyx_int_1; +static char __pyx_k___init__[] = "__init__"; +static PyObject *__pyx_kp___init__; +static char __pyx_k_setup[] = "setup"; +static PyObject *__pyx_kp_setup; +static char __pyx_k_addtoken[] = "addtoken"; +static PyObject *__pyx_kp_addtoken; +static char __pyx_k_1[] = "sphinx.pycode.nodes"; +static PyObject *__pyx_kp_1; +static char __pyx_k_Node[] = "Node"; +static PyObject *__pyx_kp_Node; +static char __pyx_k_Leaf[] = "Leaf"; +static PyObject *__pyx_kp_Leaf; +static char __pyx_k_ParseError[] = "ParseError"; +static PyObject *__pyx_kp_ParseError; +static char __pyx_k_Exception[] = "Exception"; +static PyObject *__pyx_kp_Exception; +static char __pyx_k_msg[] = "msg"; +static PyObject *__pyx_kp_msg; +static char __pyx_k_type[] = "type"; +static PyObject *__pyx_kp_type; +static char __pyx_k_value[] = "value"; +static PyObject *__pyx_kp_value; +static char __pyx_k_context[] = "context"; +static PyObject *__pyx_kp_context; +static char __pyx_k_dfas[] = "dfas"; +static PyObject *__pyx_kp_dfas; +static char __pyx_k_labels[] = "labels"; +static PyObject *__pyx_kp_labels; +static char __pyx_k_keywords[] = "keywords"; +static PyObject *__pyx_kp_keywords; +static char __pyx_k_tokens[] = "tokens"; +static PyObject *__pyx_kp_tokens; +static char __pyx_k_4[] = "number2symbol"; +static PyObject *__pyx_kp_4; +static char __pyx_k_start[] = "start"; +static PyObject *__pyx_kp_start; +static char __pyx_k_add[] = "add"; +static PyObject *__pyx_kp_add; +static char __pyx_k_get[] = "get"; +static PyObject *__pyx_kp_get; +static char __pyx_k_append[] = "append"; +static PyObject *__pyx_kp_append; +static char __pyx_k_pop[] = "pop"; +static PyObject *__pyx_kp_pop; +static char __pyx_k_used_names[] = "used_names"; +static PyObject *__pyx_kp_used_names; +static PyObject *__pyx_kp_2; +static PyObject *__pyx_builtin_Exception; +static PyObject *__pyx_kp_3; +static char __pyx_k_3[] = "%s: type=%r, value=%r, context=%r"; +static PyObject *__pyx_kp_5; +static PyObject *__pyx_kp_6; +static char __pyx_k_5[] = "too much input"; +static char __pyx_k_6[] = "bad input"; +static PyObject *__pyx_kp_7; +static char __pyx_k_7[] = "bad token"; + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":22 + * """Exception to signal the parser is stuck.""" + * + * def __init__(self, msg, type, value, context): # <<<<<<<<<<<<<< + * Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + * (msg, type, value, context)) + */ + +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_10ParseError___init__(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static PyMethodDef __pyx_mdef_6sphinx_6pycode_5pgen2_5parse_10ParseError___init__ = {"__init__", (PyCFunction)__pyx_pf_6sphinx_6pycode_5pgen2_5parse_10ParseError___init__, METH_VARARGS|METH_KEYWORDS, 0}; +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_10ParseError___init__(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { + PyObject *__pyx_v_self = 0; + PyObject *__pyx_v_msg = 0; + PyObject *__pyx_v_type = 0; + PyObject *__pyx_v_value = 0; + PyObject *__pyx_v_context = 0; + PyObject *__pyx_r; + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + static char *__pyx_argnames[] = {"self","msg","type","value","context",0}; + __pyx_self = __pyx_self; + if (likely(!__pyx_kwds) && likely(PyTuple_GET_SIZE(__pyx_args) == 5)) { + __pyx_v_self = PyTuple_GET_ITEM(__pyx_args, 0); + __pyx_v_msg = PyTuple_GET_ITEM(__pyx_args, 1); + __pyx_v_type = PyTuple_GET_ITEM(__pyx_args, 2); + __pyx_v_value = PyTuple_GET_ITEM(__pyx_args, 3); + __pyx_v_context = PyTuple_GET_ITEM(__pyx_args, 4); + } + else { + if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOOOO", __pyx_argnames, &__pyx_v_self, &__pyx_v_msg, &__pyx_v_type, &__pyx_v_value, &__pyx_v_context))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L3_error;} + } + goto __pyx_L4; + __pyx_L3_error:; + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.ParseError.__init__"); + return NULL; + __pyx_L4:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":23 + * + * def __init__(self, msg, type, value, context): + * Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % # <<<<<<<<<<<<<< + * (msg, type, value, context)) + * self.msg = msg + */ + __pyx_1 = PyObject_GetAttr(__pyx_builtin_Exception, __pyx_kp___init__); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":24 + * def __init__(self, msg, type, value, context): + * Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + * (msg, type, value, context)) # <<<<<<<<<<<<<< + * self.msg = msg + * self.type = type + */ + __pyx_2 = PyTuple_New(4); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_msg); + PyTuple_SET_ITEM(__pyx_2, 0, __pyx_v_msg); + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_2, 1, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_2, 2, __pyx_v_value); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_2, 3, __pyx_v_context); + __pyx_3 = PyNumber_Remainder(__pyx_kp_3, ((PyObject *)__pyx_2)); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_2)); __pyx_2 = 0; + __pyx_2 = PyTuple_New(2); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_self); + PyTuple_SET_ITEM(__pyx_2, 0, __pyx_v_self); + PyTuple_SET_ITEM(__pyx_2, 1, __pyx_3); + __pyx_3 = 0; + __pyx_3 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_2), NULL); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_2)); __pyx_2 = 0; + Py_DECREF(__pyx_3); __pyx_3 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":25 + * Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + * (msg, type, value, context)) + * self.msg = msg # <<<<<<<<<<<<<< + * self.type = type + * self.value = value + */ + if (PyObject_SetAttr(__pyx_v_self, __pyx_kp_msg, __pyx_v_msg) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":26 + * (msg, type, value, context)) + * self.msg = msg + * self.type = type # <<<<<<<<<<<<<< + * self.value = value + * self.context = context + */ + if (PyObject_SetAttr(__pyx_v_self, __pyx_kp_type, __pyx_v_type) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":27 + * self.msg = msg + * self.type = type + * self.value = value # <<<<<<<<<<<<<< + * self.context = context + * + */ + if (PyObject_SetAttr(__pyx_v_self, __pyx_kp_value, __pyx_v_value) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":28 + * self.type = type + * self.value = value + * self.context = context # <<<<<<<<<<<<<< + * + * + */ + if (PyObject_SetAttr(__pyx_v_self, __pyx_kp_context, __pyx_v_context) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + __pyx_r = Py_None; Py_INCREF(Py_None); + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.ParseError.__init__"); + __pyx_r = NULL; + __pyx_L0:; + return __pyx_r; +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":36 + * cdef _grammar_number2symbol + * + * def __init__(self, grammar, convert=None): # <<<<<<<<<<<<<< + * self.grammar = grammar + * #self.convert = convert or noconvert + */ + +static int __pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser___init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static int __pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser___init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { + PyObject *__pyx_v_grammar = 0; + PyObject *__pyx_v_convert = 0; + int __pyx_r; + PyObject *__pyx_1 = 0; + static char *__pyx_argnames[] = {"grammar","convert",0}; + __pyx_v_convert = Py_None; + if (likely(!__pyx_kwds) && likely(1 <= PyTuple_GET_SIZE(__pyx_args)) && likely(PyTuple_GET_SIZE(__pyx_args) <= 2)) { + __pyx_v_grammar = PyTuple_GET_ITEM(__pyx_args, 0); + if (PyTuple_GET_SIZE(__pyx_args) > 1) { + __pyx_v_convert = PyTuple_GET_ITEM(__pyx_args, 1); + } + } + else { + if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "O|O", __pyx_argnames, &__pyx_v_grammar, &__pyx_v_convert))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L3_error;} + } + goto __pyx_L4; + __pyx_L3_error:; + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.__init__"); + return -1; + __pyx_L4:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":37 + * + * def __init__(self, grammar, convert=None): + * self.grammar = grammar # <<<<<<<<<<<<<< + * #self.convert = convert or noconvert + * + */ + Py_INCREF(__pyx_v_grammar); + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->grammar); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->grammar = __pyx_v_grammar; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":40 + * #self.convert = convert or noconvert + * + * self._grammar_dfas = grammar.dfas # <<<<<<<<<<<<<< + * self._grammar_labels = grammar.labels + * self._grammar_keywords = grammar.keywords + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_grammar, __pyx_kp_dfas); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_dfas); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_dfas = __pyx_1; + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":41 + * + * self._grammar_dfas = grammar.dfas + * self._grammar_labels = grammar.labels # <<<<<<<<<<<<<< + * self._grammar_keywords = grammar.keywords + * self._grammar_tokens = grammar.tokens + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_grammar, __pyx_kp_labels); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_labels); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_labels = __pyx_1; + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":42 + * self._grammar_dfas = grammar.dfas + * self._grammar_labels = grammar.labels + * self._grammar_keywords = grammar.keywords # <<<<<<<<<<<<<< + * self._grammar_tokens = grammar.tokens + * self._grammar_number2symbol = grammar.number2symbol + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_grammar, __pyx_kp_keywords); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_keywords); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_keywords = __pyx_1; + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":43 + * self._grammar_labels = grammar.labels + * self._grammar_keywords = grammar.keywords + * self._grammar_tokens = grammar.tokens # <<<<<<<<<<<<<< + * self._grammar_number2symbol = grammar.number2symbol + * + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_grammar, __pyx_kp_tokens); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_tokens); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_tokens = __pyx_1; + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":44 + * self._grammar_keywords = grammar.keywords + * self._grammar_tokens = grammar.tokens + * self._grammar_number2symbol = grammar.number2symbol # <<<<<<<<<<<<<< + * + * def setup(self, start=None): + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_grammar, __pyx_kp_4); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_number2symbol); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_number2symbol = __pyx_1; + __pyx_1 = 0; + + __pyx_r = 0; + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.__init__"); + __pyx_r = -1; + __pyx_L0:; + return __pyx_r; +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":46 + * self._grammar_number2symbol = grammar.number2symbol + * + * def setup(self, start=None): # <<<<<<<<<<<<<< + * if start is None: + * start = self.grammar.start + */ + +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_setup(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_setup(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { + PyObject *__pyx_v_start = 0; + PyObject *__pyx_v_newnode; + PyObject *__pyx_v_stackentry; + PyObject *__pyx_r; + int __pyx_1; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + static char *__pyx_argnames[] = {"start",0}; + __pyx_v_start = Py_None; + if (likely(!__pyx_kwds) && likely(0 <= PyTuple_GET_SIZE(__pyx_args)) && likely(PyTuple_GET_SIZE(__pyx_args) <= 1)) { + if (PyTuple_GET_SIZE(__pyx_args) > 0) { + __pyx_v_start = PyTuple_GET_ITEM(__pyx_args, 0); + } + } + else { + if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "|O", __pyx_argnames, &__pyx_v_start))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 46; __pyx_clineno = __LINE__; goto __pyx_L3_error;} + } + goto __pyx_L4; + __pyx_L3_error:; + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.setup"); + return NULL; + __pyx_L4:; + Py_INCREF(__pyx_v_start); + __pyx_v_newnode = Py_None; Py_INCREF(Py_None); + __pyx_v_stackentry = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":47 + * + * def setup(self, start=None): + * if start is None: # <<<<<<<<<<<<<< + * start = self.grammar.start + * # Each stack entry is a tuple: (dfa, state, node). + */ + __pyx_1 = (__pyx_v_start == Py_None); + if (__pyx_1) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":48 + * def setup(self, start=None): + * if start is None: + * start = self.grammar.start # <<<<<<<<<<<<<< + * # Each stack entry is a tuple: (dfa, state, node). + * # A node is a tuple: (type, value, context, children), + */ + __pyx_2 = PyObject_GetAttr(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->grammar, __pyx_kp_start); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 48; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_start); + __pyx_v_start = __pyx_2; + __pyx_2 = 0; + goto __pyx_L5; + } + __pyx_L5:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":52 + * # A node is a tuple: (type, value, context, children), + * # where children is a list of nodes or None, and context may be None. + * newnode = (start, None, None, []) # <<<<<<<<<<<<<< + * stackentry = (self._grammar_dfas[start], 0, newnode) + * self.stack = [stackentry] + */ + __pyx_2 = PyList_New(0); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(4); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_start); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_start); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(__pyx_3, 1, Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(__pyx_3, 2, Py_None); + PyTuple_SET_ITEM(__pyx_3, 3, ((PyObject *)__pyx_2)); + __pyx_2 = 0; + Py_DECREF(__pyx_v_newnode); + __pyx_v_newnode = ((PyObject *)__pyx_3); + __pyx_3 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":53 + * # where children is a list of nodes or None, and context may be None. + * newnode = (start, None, None, []) + * stackentry = (self._grammar_dfas[start], 0, newnode) # <<<<<<<<<<<<<< + * self.stack = [stackentry] + * self.rootnode = None + */ + __pyx_2 = PyObject_GetItem(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_dfas, __pyx_v_start); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 53; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(3); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 53; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_2); + Py_INCREF(__pyx_int_0); + PyTuple_SET_ITEM(__pyx_3, 1, __pyx_int_0); + Py_INCREF(__pyx_v_newnode); + PyTuple_SET_ITEM(__pyx_3, 2, __pyx_v_newnode); + __pyx_2 = 0; + Py_DECREF(__pyx_v_stackentry); + __pyx_v_stackentry = ((PyObject *)__pyx_3); + __pyx_3 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":54 + * newnode = (start, None, None, []) + * stackentry = (self._grammar_dfas[start], 0, newnode) + * self.stack = [stackentry] # <<<<<<<<<<<<<< + * self.rootnode = None + * self.used_names = set() # Aliased to self.rootnode.used_names in pop() + */ + __pyx_2 = PyList_New(1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_stackentry); + PyList_SET_ITEM(__pyx_2, 0, __pyx_v_stackentry); + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack = ((PyObject *)__pyx_2); + __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":55 + * stackentry = (self._grammar_dfas[start], 0, newnode) + * self.stack = [stackentry] + * self.rootnode = None # <<<<<<<<<<<<<< + * self.used_names = set() # Aliased to self.rootnode.used_names in pop() + * + */ + Py_INCREF(Py_None); + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->rootnode); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->rootnode = Py_None; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":56 + * self.stack = [stackentry] + * self.rootnode = None + * self.used_names = set() # Aliased to self.rootnode.used_names in pop() # <<<<<<<<<<<<<< + * + * def addtoken(self, type, value, context): + */ + __pyx_3 = PyObject_Call(((PyObject*)&PySet_Type), ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 56; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->used_names); + ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->used_names = __pyx_3; + __pyx_3 = 0; + + __pyx_r = Py_None; Py_INCREF(Py_None); + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.setup"); + __pyx_r = NULL; + __pyx_L0:; + Py_DECREF(__pyx_v_newnode); + Py_DECREF(__pyx_v_stackentry); + Py_DECREF(__pyx_v_start); + return __pyx_r; +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":58 + * self.used_names = set() # Aliased to self.rootnode.used_names in pop() + * + * def addtoken(self, type, value, context): # <<<<<<<<<<<<<< + * """Add a token; return True iff this is the end of the program.""" + * cdef int ilabel, i, t, state, newstate + */ + +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_addtoken(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static char __pyx_doc_6sphinx_6pycode_5pgen2_5parse_6Parser_addtoken[] = "Add a token; return True iff this is the end of the program."; +static PyObject *__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_addtoken(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { + PyObject *__pyx_v_type = 0; + PyObject *__pyx_v_value = 0; + PyObject *__pyx_v_context = 0; + int __pyx_v_ilabel; + int __pyx_v_i; + int __pyx_v_t; + int __pyx_v_state; + int __pyx_v_newstate; + PyObject *__pyx_v_dfa; + PyObject *__pyx_v_node; + PyObject *__pyx_v_states; + PyObject *__pyx_v_first; + PyObject *__pyx_v_arcs; + PyObject *__pyx_v_v; + PyObject *__pyx_v_itsdfa; + PyObject *__pyx_v_itsstates; + PyObject *__pyx_v_itsfirst; + PyObject *__pyx_r; + int __pyx_1; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + PyObject *__pyx_4 = 0; + int __pyx_5; + Py_ssize_t __pyx_6 = 0; + PyObject *__pyx_7 = 0; + int __pyx_8; + static char *__pyx_argnames[] = {"type","value","context",0}; + if (likely(!__pyx_kwds) && likely(PyTuple_GET_SIZE(__pyx_args) == 3)) { + __pyx_v_type = PyTuple_GET_ITEM(__pyx_args, 0); + __pyx_v_value = PyTuple_GET_ITEM(__pyx_args, 1); + __pyx_v_context = PyTuple_GET_ITEM(__pyx_args, 2); + } + else { + if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOO", __pyx_argnames, &__pyx_v_type, &__pyx_v_value, &__pyx_v_context))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 58; __pyx_clineno = __LINE__; goto __pyx_L3_error;} + } + goto __pyx_L4; + __pyx_L3_error:; + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.addtoken"); + return NULL; + __pyx_L4:; + __pyx_v_dfa = Py_None; Py_INCREF(Py_None); + __pyx_v_node = Py_None; Py_INCREF(Py_None); + __pyx_v_states = Py_None; Py_INCREF(Py_None); + __pyx_v_first = Py_None; Py_INCREF(Py_None); + __pyx_v_arcs = Py_None; Py_INCREF(Py_None); + __pyx_v_v = Py_None; Py_INCREF(Py_None); + __pyx_v_itsdfa = Py_None; Py_INCREF(Py_None); + __pyx_v_itsstates = Py_None; Py_INCREF(Py_None); + __pyx_v_itsfirst = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":62 + * cdef int ilabel, i, t, state, newstate + * # Map from token to label + * ilabel = self.classify(type, value, context) # <<<<<<<<<<<<<< + * # Loop until the token is shifted; may raise exceptions + * while True: + */ + __pyx_v_ilabel = ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->__pyx_vtab)->classify(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self), __pyx_v_type, __pyx_v_value, __pyx_v_context); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":64 + * ilabel = self.classify(type, value, context) + * # Loop until the token is shifted; may raise exceptions + * while True: # <<<<<<<<<<<<<< + * dfa, state, node = self.stack[-1] + * states, first = dfa + */ + while (1) { + __pyx_1 = 1; + if (!__pyx_1) break; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":65 + * # Loop until the token is shifted; may raise exceptions + * while True: + * dfa, state, node = self.stack[-1] # <<<<<<<<<<<<<< + * states, first = dfa + * arcs = states[state] + */ + __pyx_2 = __Pyx_GetItemInt(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack, -1, 0); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_2) && PyTuple_GET_SIZE(__pyx_2) == 3) { + PyObject* tuple = __pyx_2; + __pyx_4 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_4); + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_4; + __pyx_4 = 0; + __pyx_4 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_4); + __pyx_5 = __pyx_PyInt_int(__pyx_4); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + __pyx_v_state = __pyx_5; + __pyx_4 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_4); + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_4; + __pyx_4 = 0; + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + else { + __pyx_3 = PyObject_GetIter(__pyx_2); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_4 = __Pyx_UnpackItem(__pyx_3, 0); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_4; + __pyx_4 = 0; + __pyx_4 = __Pyx_UnpackItem(__pyx_3, 1); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_5 = __pyx_PyInt_int(__pyx_4); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + __pyx_v_state = __pyx_5; + __pyx_4 = __Pyx_UnpackItem(__pyx_3, 2); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_4; + __pyx_4 = 0; + if (__Pyx_EndUnpack(__pyx_3) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_3); __pyx_3 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":66 + * while True: + * dfa, state, node = self.stack[-1] + * states, first = dfa # <<<<<<<<<<<<<< + * arcs = states[state] + * # Look for a state with this label + */ + if (PyTuple_CheckExact(__pyx_v_dfa) && PyTuple_GET_SIZE(__pyx_v_dfa) == 2) { + PyObject* tuple = __pyx_v_dfa; + __pyx_2 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_states); + __pyx_v_states = __pyx_2; + __pyx_2 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_first); + __pyx_v_first = __pyx_3; + __pyx_3 = 0; + } + else { + __pyx_4 = PyObject_GetIter(__pyx_v_dfa); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = __Pyx_UnpackItem(__pyx_4, 0); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_states); + __pyx_v_states = __pyx_2; + __pyx_2 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_4, 1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_first); + __pyx_v_first = __pyx_3; + __pyx_3 = 0; + if (__Pyx_EndUnpack(__pyx_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 66; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":67 + * dfa, state, node = self.stack[-1] + * states, first = dfa + * arcs = states[state] # <<<<<<<<<<<<<< + * # Look for a state with this label + * for i, newstate in arcs: + */ + __pyx_2 = __Pyx_GetItemInt(__pyx_v_states, __pyx_v_state, 0); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 67; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_arcs); + __pyx_v_arcs = __pyx_2; + __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":69 + * arcs = states[state] + * # Look for a state with this label + * for i, newstate in arcs: # <<<<<<<<<<<<<< + * t, v = self._grammar_labels[i] + * if ilabel == i: + */ + if (PyList_CheckExact(__pyx_v_arcs) || PyTuple_CheckExact(__pyx_v_arcs)) { + __pyx_6 = 0; __pyx_3 = __pyx_v_arcs; Py_INCREF(__pyx_3); + } else { + __pyx_6 = -1; __pyx_3 = PyObject_GetIter(__pyx_v_arcs); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + } + for (;;) { + if (likely(PyList_CheckExact(__pyx_3))) { + if (__pyx_6 >= PyList_GET_SIZE(__pyx_3)) break; + __pyx_4 = PyList_GET_ITEM(__pyx_3, __pyx_6); Py_INCREF(__pyx_4); __pyx_6++; + } else if (likely(PyTuple_CheckExact(__pyx_3))) { + if (__pyx_6 >= PyTuple_GET_SIZE(__pyx_3)) break; + __pyx_4 = PyTuple_GET_ITEM(__pyx_3, __pyx_6); Py_INCREF(__pyx_4); __pyx_6++; + } else { + __pyx_4 = PyIter_Next(__pyx_3); + if (!__pyx_4) { + if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + break; + } + } + if (PyTuple_CheckExact(__pyx_4) && PyTuple_GET_SIZE(__pyx_4) == 2) { + PyObject* tuple = __pyx_4; + __pyx_7 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_7); + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_i = __pyx_5; + __pyx_7 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_7); + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_newstate = __pyx_5; + Py_DECREF(__pyx_4); __pyx_4 = 0; + } + else { + __pyx_2 = PyObject_GetIter(__pyx_4); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + __pyx_7 = __Pyx_UnpackItem(__pyx_2, 0); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_i = __pyx_5; + __pyx_7 = __Pyx_UnpackItem(__pyx_2, 1); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_newstate = __pyx_5; + if (__Pyx_EndUnpack(__pyx_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":70 + * # Look for a state with this label + * for i, newstate in arcs: + * t, v = self._grammar_labels[i] # <<<<<<<<<<<<<< + * if ilabel == i: + * # Look it up in the list of labels + */ + __pyx_7 = __Pyx_GetItemInt(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_labels, __pyx_v_i, 0); if (!__pyx_7) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_7) && PyTuple_GET_SIZE(__pyx_7) == 2) { + PyObject* tuple = __pyx_7; + __pyx_2 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_2); + __pyx_5 = __pyx_PyInt_int(__pyx_2); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_v_t = __pyx_5; + __pyx_2 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_v); + __pyx_v_v = __pyx_2; + __pyx_2 = 0; + Py_DECREF(__pyx_7); __pyx_7 = 0; + } + else { + __pyx_4 = PyObject_GetIter(__pyx_7); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_4, 0); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_5 = __pyx_PyInt_int(__pyx_2); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_v_t = __pyx_5; + __pyx_2 = __Pyx_UnpackItem(__pyx_4, 1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_v); + __pyx_v_v = __pyx_2; + __pyx_2 = 0; + if (__Pyx_EndUnpack(__pyx_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":71 + * for i, newstate in arcs: + * t, v = self._grammar_labels[i] + * if ilabel == i: # <<<<<<<<<<<<<< + * # Look it up in the list of labels + * ## assert t < 256 + */ + __pyx_1 = (__pyx_v_ilabel == __pyx_v_i); + if (__pyx_1) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":75 + * ## assert t < 256 + * # Shift a token; we're done with it + * self.shift(type, value, newstate, context) # <<<<<<<<<<<<<< + * # Pop while we are in an accept-only state + * state = newstate + */ + __pyx_2 = PyInt_FromLong(__pyx_v_newstate); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 75; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->__pyx_vtab)->shift(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self), __pyx_v_type, __pyx_v_value, __pyx_2, __pyx_v_context); + Py_DECREF(__pyx_2); __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":77 + * self.shift(type, value, newstate, context) + * # Pop while we are in an accept-only state + * state = newstate # <<<<<<<<<<<<<< + * while states[state] == [(0, state)]: + * self.pop() + */ + __pyx_v_state = __pyx_v_newstate; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":78 + * # Pop while we are in an accept-only state + * state = newstate + * while states[state] == [(0, state)]: # <<<<<<<<<<<<<< + * self.pop() + * if not self.stack: + */ + while (1) { + __pyx_7 = __Pyx_GetItemInt(__pyx_v_states, __pyx_v_state, 0); if (!__pyx_7) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_4 = PyInt_FromLong(__pyx_v_state); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = PyTuple_New(2); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_int_0); + PyTuple_SET_ITEM(__pyx_2, 0, __pyx_int_0); + PyTuple_SET_ITEM(__pyx_2, 1, __pyx_4); + __pyx_4 = 0; + __pyx_4 = PyList_New(1); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + PyList_SET_ITEM(__pyx_4, 0, ((PyObject *)__pyx_2)); + __pyx_2 = 0; + __pyx_2 = PyObject_RichCompare(__pyx_7, ((PyObject *)__pyx_4), Py_EQ); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + Py_DECREF(((PyObject *)__pyx_4)); __pyx_4 = 0; + __pyx_1 = __Pyx_PyObject_IsTrue(__pyx_2); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + if (!__pyx_1) break; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":79 + * state = newstate + * while states[state] == [(0, state)]: + * self.pop() # <<<<<<<<<<<<<< + * if not self.stack: + * # Done parsing! + */ + ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->__pyx_vtab)->pop(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":80 + * while states[state] == [(0, state)]: + * self.pop() + * if not self.stack: # <<<<<<<<<<<<<< + * # Done parsing! + * return True + */ + __pyx_1 = __Pyx_PyObject_IsTrue(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 80; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_8 = (!__pyx_1); + if (__pyx_8) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":82 + * if not self.stack: + * # Done parsing! + * return True # <<<<<<<<<<<<<< + * dfa, state, node = self.stack[-1] + * states, first = dfa + */ + __pyx_7 = __Pyx_PyBool_FromLong(1); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 82; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_r = __pyx_7; + __pyx_7 = 0; + Py_DECREF(__pyx_3); __pyx_3 = 0; + goto __pyx_L0; + goto __pyx_L12; + } + __pyx_L12:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":83 + * # Done parsing! + * return True + * dfa, state, node = self.stack[-1] # <<<<<<<<<<<<<< + * states, first = dfa + * # Done with this token + */ + __pyx_4 = __Pyx_GetItemInt(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack, -1, 0); if (!__pyx_4) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_4) && PyTuple_GET_SIZE(__pyx_4) == 3) { + PyObject* tuple = __pyx_4; + __pyx_7 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_7); + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_7; + __pyx_7 = 0; + __pyx_7 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_7); + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_state = __pyx_5; + __pyx_7 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_7); + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_7; + __pyx_7 = 0; + Py_DECREF(__pyx_4); __pyx_4 = 0; + } + else { + __pyx_2 = PyObject_GetIter(__pyx_4); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + __pyx_7 = __Pyx_UnpackItem(__pyx_2, 0); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_7; + __pyx_7 = 0; + __pyx_7 = __Pyx_UnpackItem(__pyx_2, 1); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_5 = __pyx_PyInt_int(__pyx_7); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + __pyx_v_state = __pyx_5; + __pyx_7 = __Pyx_UnpackItem(__pyx_2, 2); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_7; + __pyx_7 = 0; + if (__Pyx_EndUnpack(__pyx_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 83; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":84 + * return True + * dfa, state, node = self.stack[-1] + * states, first = dfa # <<<<<<<<<<<<<< + * # Done with this token + * return False + */ + if (PyTuple_CheckExact(__pyx_v_dfa) && PyTuple_GET_SIZE(__pyx_v_dfa) == 2) { + PyObject* tuple = __pyx_v_dfa; + __pyx_4 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_4); + Py_DECREF(__pyx_v_states); + __pyx_v_states = __pyx_4; + __pyx_4 = 0; + __pyx_2 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_first); + __pyx_v_first = __pyx_2; + __pyx_2 = 0; + } + else { + __pyx_7 = PyObject_GetIter(__pyx_v_dfa); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 84; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_4 = __Pyx_UnpackItem(__pyx_7, 0); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 84; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_states); + __pyx_v_states = __pyx_4; + __pyx_4 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_7, 1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 84; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_first); + __pyx_v_first = __pyx_2; + __pyx_2 = 0; + if (__Pyx_EndUnpack(__pyx_7) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 84; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + } + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":86 + * states, first = dfa + * # Done with this token + * return False # <<<<<<<<<<<<<< + * elif t >= 256: + * # See if it's a symbol and if we're in its first set + */ + __pyx_4 = __Pyx_PyBool_FromLong(0); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 86; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_r = __pyx_4; + __pyx_4 = 0; + Py_DECREF(__pyx_3); __pyx_3 = 0; + goto __pyx_L0; + goto __pyx_L9; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":87 + * # Done with this token + * return False + * elif t >= 256: # <<<<<<<<<<<<<< + * # See if it's a symbol and if we're in its first set + * itsdfa = self._grammar_dfas[t] + */ + __pyx_1 = (__pyx_v_t >= 256); + if (__pyx_1) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":89 + * elif t >= 256: + * # See if it's a symbol and if we're in its first set + * itsdfa = self._grammar_dfas[t] # <<<<<<<<<<<<<< + * itsstates, itsfirst = itsdfa + * if ilabel in itsfirst: + */ + __pyx_2 = __Pyx_GetItemInt(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->_grammar_dfas, __pyx_v_t, 0); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 89; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_itsdfa); + __pyx_v_itsdfa = __pyx_2; + __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":90 + * # See if it's a symbol and if we're in its first set + * itsdfa = self._grammar_dfas[t] + * itsstates, itsfirst = itsdfa # <<<<<<<<<<<<<< + * if ilabel in itsfirst: + * # Push a symbol + */ + if (PyTuple_CheckExact(__pyx_v_itsdfa) && PyTuple_GET_SIZE(__pyx_v_itsdfa) == 2) { + PyObject* tuple = __pyx_v_itsdfa; + __pyx_4 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_4); + Py_DECREF(__pyx_v_itsstates); + __pyx_v_itsstates = __pyx_4; + __pyx_4 = 0; + __pyx_2 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_itsfirst); + __pyx_v_itsfirst = __pyx_2; + __pyx_2 = 0; + } + else { + __pyx_7 = PyObject_GetIter(__pyx_v_itsdfa); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_4 = __Pyx_UnpackItem(__pyx_7, 0); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_itsstates); + __pyx_v_itsstates = __pyx_4; + __pyx_4 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_7, 1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_itsfirst); + __pyx_v_itsfirst = __pyx_2; + __pyx_2 = 0; + if (__Pyx_EndUnpack(__pyx_7) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 90; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":91 + * itsdfa = self._grammar_dfas[t] + * itsstates, itsfirst = itsdfa + * if ilabel in itsfirst: # <<<<<<<<<<<<<< + * # Push a symbol + * self.push(t, itsdfa, newstate, context) + */ + __pyx_4 = PyInt_FromLong(__pyx_v_ilabel); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_8 = (PySequence_Contains(__pyx_v_itsfirst, __pyx_4)); if (unlikely(__pyx_8 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + if (__pyx_8) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":93 + * if ilabel in itsfirst: + * # Push a symbol + * self.push(t, itsdfa, newstate, context) # <<<<<<<<<<<<<< + * break # To continue the outer while loop + * else: + */ + __pyx_2 = PyInt_FromLong(__pyx_v_t); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_7 = PyInt_FromLong(__pyx_v_newstate); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 93; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->__pyx_vtab)->push(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self), __pyx_2, __pyx_v_itsdfa, __pyx_7, __pyx_v_context); + Py_DECREF(__pyx_2); __pyx_2 = 0; + Py_DECREF(__pyx_7); __pyx_7 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":94 + * # Push a symbol + * self.push(t, itsdfa, newstate, context) + * break # To continue the outer while loop # <<<<<<<<<<<<<< + * else: + * if (0, state) in arcs: + */ + goto __pyx_L8; + goto __pyx_L13; + } + __pyx_L13:; + goto __pyx_L9; + } + __pyx_L9:; + } + /*else*/ { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":96 + * break # To continue the outer while loop + * else: + * if (0, state) in arcs: # <<<<<<<<<<<<<< + * # An accepting state, pop it and try something else + * self.pop() + */ + __pyx_4 = PyInt_FromLong(__pyx_v_state); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = PyTuple_New(2); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_int_0); + PyTuple_SET_ITEM(__pyx_2, 0, __pyx_int_0); + PyTuple_SET_ITEM(__pyx_2, 1, __pyx_4); + __pyx_4 = 0; + __pyx_1 = (PySequence_Contains(__pyx_v_arcs, ((PyObject *)__pyx_2))); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_2)); __pyx_2 = 0; + if (__pyx_1) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":98 + * if (0, state) in arcs: + * # An accepting state, pop it and try something else + * self.pop() # <<<<<<<<<<<<<< + * if not self.stack: + * # Done parsing, but another token is input + */ + ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->__pyx_vtab)->pop(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":99 + * # An accepting state, pop it and try something else + * self.pop() + * if not self.stack: # <<<<<<<<<<<<<< + * # Done parsing, but another token is input + * raise ParseError("too much input", + */ + __pyx_8 = __Pyx_PyObject_IsTrue(((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self)->stack); if (unlikely(__pyx_8 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 99; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_1 = (!__pyx_8); + if (__pyx_1) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":101 + * if not self.stack: + * # Done parsing, but another token is input + * raise ParseError("too much input", # <<<<<<<<<<<<<< + * type, value, context) + * else: + */ + __pyx_7 = __Pyx_GetName(__pyx_m, __pyx_kp_ParseError); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":102 + * # Done parsing, but another token is input + * raise ParseError("too much input", + * type, value, context) # <<<<<<<<<<<<<< + * else: + * # No success finding a transition + */ + __pyx_4 = PyTuple_New(4); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_kp_5); + PyTuple_SET_ITEM(__pyx_4, 0, __pyx_kp_5); + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_4, 1, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_4, 2, __pyx_v_value); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_4, 3, __pyx_v_context); + __pyx_2 = PyObject_Call(__pyx_7, ((PyObject *)__pyx_4), NULL); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + Py_DECREF(((PyObject *)__pyx_4)); __pyx_4 = 0; + __Pyx_Raise(__pyx_2, 0, 0); + Py_DECREF(__pyx_2); __pyx_2 = 0; + {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + goto __pyx_L15; + } + __pyx_L15:; + goto __pyx_L14; + } + /*else*/ { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":105 + * else: + * # No success finding a transition + * raise ParseError("bad input", type, value, context) # <<<<<<<<<<<<<< + * + * cdef int classify(self, type, value, context): + */ + __pyx_7 = __Pyx_GetName(__pyx_m, __pyx_kp_ParseError); if (unlikely(!__pyx_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_4 = PyTuple_New(4); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_kp_6); + PyTuple_SET_ITEM(__pyx_4, 0, __pyx_kp_6); + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_4, 1, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_4, 2, __pyx_v_value); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_4, 3, __pyx_v_context); + __pyx_2 = PyObject_Call(__pyx_7, ((PyObject *)__pyx_4), NULL); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_7); __pyx_7 = 0; + Py_DECREF(((PyObject *)__pyx_4)); __pyx_4 = 0; + __Pyx_Raise(__pyx_2, 0, 0); + Py_DECREF(__pyx_2); __pyx_2 = 0; + {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + } + __pyx_L14:; + } + __pyx_L8:; + Py_DECREF(__pyx_3); __pyx_3 = 0; + } + + __pyx_r = Py_None; Py_INCREF(Py_None); + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + Py_XDECREF(__pyx_4); + Py_XDECREF(__pyx_7); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.addtoken"); + __pyx_r = NULL; + __pyx_L0:; + Py_DECREF(__pyx_v_dfa); + Py_DECREF(__pyx_v_node); + Py_DECREF(__pyx_v_states); + Py_DECREF(__pyx_v_first); + Py_DECREF(__pyx_v_arcs); + Py_DECREF(__pyx_v_v); + Py_DECREF(__pyx_v_itsdfa); + Py_DECREF(__pyx_v_itsstates); + Py_DECREF(__pyx_v_itsfirst); + return __pyx_r; +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":107 + * raise ParseError("bad input", type, value, context) + * + * cdef int classify(self, type, value, context): # <<<<<<<<<<<<<< + * """Turn a token into a label. (Internal)""" + * if type == NAME: + */ + +static int __pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_classify(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_v_self, PyObject *__pyx_v_type, PyObject *__pyx_v_value, PyObject *__pyx_v_context) { + PyObject *__pyx_v_ilabel; + int __pyx_r; + PyObject *__pyx_1 = 0; + int __pyx_2; + PyObject *__pyx_3 = 0; + PyObject *__pyx_4 = 0; + int __pyx_5; + __pyx_v_ilabel = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":109 + * cdef int classify(self, type, value, context): + * """Turn a token into a label. (Internal)""" + * if type == NAME: # <<<<<<<<<<<<<< + * # Keep a listing of all used names + * self.used_names.add(value) + */ + __pyx_1 = PyObject_RichCompare(__pyx_v_type, __pyx_int_1, Py_EQ); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 109; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = __Pyx_PyObject_IsTrue(__pyx_1); if (unlikely(__pyx_2 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 109; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + if (__pyx_2) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":111 + * if type == NAME: + * # Keep a listing of all used names + * self.used_names.add(value) # <<<<<<<<<<<<<< + * # Check for reserved words + * ilabel = self._grammar_keywords.get(value) + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_self->used_names, __pyx_kp_add); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 111; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 111; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_value); + __pyx_4 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_3), NULL); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 111; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_3)); __pyx_3 = 0; + Py_DECREF(__pyx_4); __pyx_4 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":113 + * self.used_names.add(value) + * # Check for reserved words + * ilabel = self._grammar_keywords.get(value) # <<<<<<<<<<<<<< + * if ilabel is not None: + * return ilabel + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_self->_grammar_keywords, __pyx_kp_get); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_value); + __pyx_4 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_3), NULL); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 113; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_3)); __pyx_3 = 0; + Py_DECREF(__pyx_v_ilabel); + __pyx_v_ilabel = __pyx_4; + __pyx_4 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":114 + * # Check for reserved words + * ilabel = self._grammar_keywords.get(value) + * if ilabel is not None: # <<<<<<<<<<<<<< + * return ilabel + * ilabel = self._grammar_tokens.get(type) + */ + __pyx_2 = (__pyx_v_ilabel != Py_None); + if (__pyx_2) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":115 + * ilabel = self._grammar_keywords.get(value) + * if ilabel is not None: + * return ilabel # <<<<<<<<<<<<<< + * ilabel = self._grammar_tokens.get(type) + * if ilabel is None: + */ + __pyx_5 = __pyx_PyInt_int(__pyx_v_ilabel); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 115; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_r = __pyx_5; + goto __pyx_L0; + goto __pyx_L4; + } + __pyx_L4:; + goto __pyx_L3; + } + __pyx_L3:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":116 + * if ilabel is not None: + * return ilabel + * ilabel = self._grammar_tokens.get(type) # <<<<<<<<<<<<<< + * if ilabel is None: + * raise ParseError("bad token", type, value, context) + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_self->_grammar_tokens, __pyx_kp_get); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 116; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 116; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_type); + __pyx_4 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_3), NULL); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 116; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_3)); __pyx_3 = 0; + Py_DECREF(__pyx_v_ilabel); + __pyx_v_ilabel = __pyx_4; + __pyx_4 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":117 + * return ilabel + * ilabel = self._grammar_tokens.get(type) + * if ilabel is None: # <<<<<<<<<<<<<< + * raise ParseError("bad token", type, value, context) + * return ilabel + */ + __pyx_2 = (__pyx_v_ilabel == Py_None); + if (__pyx_2) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":118 + * ilabel = self._grammar_tokens.get(type) + * if ilabel is None: + * raise ParseError("bad token", type, value, context) # <<<<<<<<<<<<<< + * return ilabel + * + */ + __pyx_1 = __Pyx_GetName(__pyx_m, __pyx_kp_ParseError); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = PyTuple_New(4); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_kp_7); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_kp_7); + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_3, 1, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_3, 2, __pyx_v_value); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_3, 3, __pyx_v_context); + __pyx_4 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_3), NULL); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_3)); __pyx_3 = 0; + __Pyx_Raise(__pyx_4, 0, 0); + Py_DECREF(__pyx_4); __pyx_4 = 0; + {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + goto __pyx_L5; + } + __pyx_L5:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":119 + * if ilabel is None: + * raise ParseError("bad token", type, value, context) + * return ilabel # <<<<<<<<<<<<<< + * + * cdef void shift(self, type, value, newstate, context): + */ + __pyx_5 = __pyx_PyInt_int(__pyx_v_ilabel); if (unlikely((__pyx_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 119; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_r = __pyx_5; + goto __pyx_L0; + + __pyx_r = 0; + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_3); + Py_XDECREF(__pyx_4); + __Pyx_WriteUnraisable("sphinx.pycode.pgen2.parse.Parser.classify"); + __pyx_r = 0; + __pyx_L0:; + Py_DECREF(__pyx_v_ilabel); + return __pyx_r; +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":121 + * return ilabel + * + * cdef void shift(self, type, value, newstate, context): # <<<<<<<<<<<<<< + * """Shift a token. (Internal)""" + * dfa, state, node = self.stack[-1] + */ + +static void __pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_shift(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_v_self, PyObject *__pyx_v_type, PyObject *__pyx_v_value, PyObject *__pyx_v_newstate, PyObject *__pyx_v_context) { + PyObject *__pyx_v_dfa; + PyObject *__pyx_v_state; + PyObject *__pyx_v_node; + PyObject *__pyx_v_newnode; + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + int __pyx_4; + __pyx_v_dfa = Py_None; Py_INCREF(Py_None); + __pyx_v_state = Py_None; Py_INCREF(Py_None); + __pyx_v_node = Py_None; Py_INCREF(Py_None); + __pyx_v_newnode = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":123 + * cdef void shift(self, type, value, newstate, context): + * """Shift a token. (Internal)""" + * dfa, state, node = self.stack[-1] # <<<<<<<<<<<<<< + * newnode = (type, value, context, None) + * newnode = self.convert(newnode) + */ + __pyx_1 = __Pyx_GetItemInt(__pyx_v_self->stack, -1, 0); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_1) && PyTuple_GET_SIZE(__pyx_1) == 3) { + PyObject* tuple = __pyx_1; + __pyx_3 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + Py_DECREF(__pyx_1); __pyx_1 = 0; + } + else { + __pyx_2 = PyObject_GetIter(__pyx_1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 0); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 2); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + if (__Pyx_EndUnpack(__pyx_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":124 + * """Shift a token. (Internal)""" + * dfa, state, node = self.stack[-1] + * newnode = (type, value, context, None) # <<<<<<<<<<<<<< + * newnode = self.convert(newnode) + * if newnode is not None: + */ + __pyx_3 = PyTuple_New(4); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_3, 1, __pyx_v_value); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_3, 2, __pyx_v_context); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(__pyx_3, 3, Py_None); + Py_DECREF(__pyx_v_newnode); + __pyx_v_newnode = ((PyObject *)__pyx_3); + __pyx_3 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":125 + * dfa, state, node = self.stack[-1] + * newnode = (type, value, context, None) + * newnode = self.convert(newnode) # <<<<<<<<<<<<<< + * if newnode is not None: + * node[-1].append(newnode) + */ + __pyx_1 = ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self->__pyx_vtab)->convert(__pyx_v_self, __pyx_v_newnode); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 125; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_newnode); + __pyx_v_newnode = __pyx_1; + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":126 + * newnode = (type, value, context, None) + * newnode = self.convert(newnode) + * if newnode is not None: # <<<<<<<<<<<<<< + * node[-1].append(newnode) + * self.stack[-1] = (dfa, newstate, node) + */ + __pyx_4 = (__pyx_v_newnode != Py_None); + if (__pyx_4) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":127 + * newnode = self.convert(newnode) + * if newnode is not None: + * node[-1].append(newnode) # <<<<<<<<<<<<<< + * self.stack[-1] = (dfa, newstate, node) + * + */ + __pyx_2 = __Pyx_GetItemInt(__pyx_v_node, -1, 0); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 127; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = __Pyx_PyObject_Append(__pyx_2, __pyx_v_newnode); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 127; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + Py_DECREF(__pyx_3); __pyx_3 = 0; + goto __pyx_L3; + } + __pyx_L3:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":128 + * if newnode is not None: + * node[-1].append(newnode) + * self.stack[-1] = (dfa, newstate, node) # <<<<<<<<<<<<<< + * + * cdef void push(self, type, newdfa, newstate, context): + */ + __pyx_1 = PyTuple_New(3); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 128; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_dfa); + PyTuple_SET_ITEM(__pyx_1, 0, __pyx_v_dfa); + Py_INCREF(__pyx_v_newstate); + PyTuple_SET_ITEM(__pyx_1, 1, __pyx_v_newstate); + Py_INCREF(__pyx_v_node); + PyTuple_SET_ITEM(__pyx_1, 2, __pyx_v_node); + if (__Pyx_SetItemInt(__pyx_v_self->stack, -1, ((PyObject *)__pyx_1), 0) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 128; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_1)); __pyx_1 = 0; + + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + __Pyx_WriteUnraisable("sphinx.pycode.pgen2.parse.Parser.shift"); + __pyx_L0:; + Py_DECREF(__pyx_v_dfa); + Py_DECREF(__pyx_v_state); + Py_DECREF(__pyx_v_node); + Py_DECREF(__pyx_v_newnode); +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":130 + * self.stack[-1] = (dfa, newstate, node) + * + * cdef void push(self, type, newdfa, newstate, context): # <<<<<<<<<<<<<< + * """Push a nonterminal. (Internal)""" + * dfa, state, node = self.stack[-1] + */ + +static void __pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_push(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_v_self, PyObject *__pyx_v_type, PyObject *__pyx_v_newdfa, PyObject *__pyx_v_newstate, PyObject *__pyx_v_context) { + PyObject *__pyx_v_dfa; + PyObject *__pyx_v_state; + PyObject *__pyx_v_node; + PyObject *__pyx_v_newnode; + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + __pyx_v_dfa = Py_None; Py_INCREF(Py_None); + __pyx_v_state = Py_None; Py_INCREF(Py_None); + __pyx_v_node = Py_None; Py_INCREF(Py_None); + __pyx_v_newnode = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":132 + * cdef void push(self, type, newdfa, newstate, context): + * """Push a nonterminal. (Internal)""" + * dfa, state, node = self.stack[-1] # <<<<<<<<<<<<<< + * newnode = (type, None, context, []) + * self.stack[-1] = (dfa, newstate, node) + */ + __pyx_1 = __Pyx_GetItemInt(__pyx_v_self->stack, -1, 0); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_1) && PyTuple_GET_SIZE(__pyx_1) == 3) { + PyObject* tuple = __pyx_1; + __pyx_3 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + Py_DECREF(__pyx_1); __pyx_1 = 0; + } + else { + __pyx_2 = PyObject_GetIter(__pyx_1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 0); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_2, 2); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + if (__Pyx_EndUnpack(__pyx_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":133 + * """Push a nonterminal. (Internal)""" + * dfa, state, node = self.stack[-1] + * newnode = (type, None, context, []) # <<<<<<<<<<<<<< + * self.stack[-1] = (dfa, newstate, node) + * self.stack.append((newdfa, 0, newnode)) + */ + __pyx_3 = PyList_New(0); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_1 = PyTuple_New(4); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_1, 0, __pyx_v_type); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(__pyx_1, 1, Py_None); + Py_INCREF(__pyx_v_context); + PyTuple_SET_ITEM(__pyx_1, 2, __pyx_v_context); + PyTuple_SET_ITEM(__pyx_1, 3, ((PyObject *)__pyx_3)); + __pyx_3 = 0; + Py_DECREF(__pyx_v_newnode); + __pyx_v_newnode = ((PyObject *)__pyx_1); + __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":134 + * dfa, state, node = self.stack[-1] + * newnode = (type, None, context, []) + * self.stack[-1] = (dfa, newstate, node) # <<<<<<<<<<<<<< + * self.stack.append((newdfa, 0, newnode)) + * + */ + __pyx_2 = PyTuple_New(3); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 134; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_dfa); + PyTuple_SET_ITEM(__pyx_2, 0, __pyx_v_dfa); + Py_INCREF(__pyx_v_newstate); + PyTuple_SET_ITEM(__pyx_2, 1, __pyx_v_newstate); + Py_INCREF(__pyx_v_node); + PyTuple_SET_ITEM(__pyx_2, 2, __pyx_v_node); + if (__Pyx_SetItemInt(__pyx_v_self->stack, -1, ((PyObject *)__pyx_2), 0) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 134; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_2)); __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":135 + * newnode = (type, None, context, []) + * self.stack[-1] = (dfa, newstate, node) + * self.stack.append((newdfa, 0, newnode)) # <<<<<<<<<<<<<< + * + * cdef void pop(self): + */ + __pyx_3 = PyTuple_New(3); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 135; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_newdfa); + PyTuple_SET_ITEM(__pyx_3, 0, __pyx_v_newdfa); + Py_INCREF(__pyx_int_0); + PyTuple_SET_ITEM(__pyx_3, 1, __pyx_int_0); + Py_INCREF(__pyx_v_newnode); + PyTuple_SET_ITEM(__pyx_3, 2, __pyx_v_newnode); + __pyx_1 = __Pyx_PyObject_Append(__pyx_v_self->stack, ((PyObject *)__pyx_3)); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 135; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_3)); __pyx_3 = 0; + Py_DECREF(__pyx_1); __pyx_1 = 0; + + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + __Pyx_WriteUnraisable("sphinx.pycode.pgen2.parse.Parser.push"); + __pyx_L0:; + Py_DECREF(__pyx_v_dfa); + Py_DECREF(__pyx_v_state); + Py_DECREF(__pyx_v_node); + Py_DECREF(__pyx_v_newnode); +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":137 + * self.stack.append((newdfa, 0, newnode)) + * + * cdef void pop(self): # <<<<<<<<<<<<<< + * """Pop a nonterminal. (Internal)""" + * popdfa, popstate, popnode = self.stack.pop() + */ + +static void __pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_pop(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_v_self) { + PyObject *__pyx_v_popdfa; + PyObject *__pyx_v_popstate; + PyObject *__pyx_v_popnode; + PyObject *__pyx_v_newnode; + PyObject *__pyx_v_dfa; + PyObject *__pyx_v_state; + PyObject *__pyx_v_node; + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + int __pyx_4; + __pyx_v_popdfa = Py_None; Py_INCREF(Py_None); + __pyx_v_popstate = Py_None; Py_INCREF(Py_None); + __pyx_v_popnode = Py_None; Py_INCREF(Py_None); + __pyx_v_newnode = Py_None; Py_INCREF(Py_None); + __pyx_v_dfa = Py_None; Py_INCREF(Py_None); + __pyx_v_state = Py_None; Py_INCREF(Py_None); + __pyx_v_node = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":139 + * cdef void pop(self): + * """Pop a nonterminal. (Internal)""" + * popdfa, popstate, popnode = self.stack.pop() # <<<<<<<<<<<<<< + * newnode = self.convert(popnode) + * if newnode is not None: + */ + __pyx_1 = PyObject_GetAttr(__pyx_v_self->stack, __pyx_kp_pop); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = PyObject_Call(__pyx_1, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + if (PyTuple_CheckExact(__pyx_2) && PyTuple_GET_SIZE(__pyx_2) == 3) { + PyObject* tuple = __pyx_2; + __pyx_3 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_popdfa); + __pyx_v_popdfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_popstate); + __pyx_v_popstate = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_popnode); + __pyx_v_popnode = __pyx_3; + __pyx_3 = 0; + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + else { + __pyx_1 = PyObject_GetIter(__pyx_2); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 0); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_popdfa); + __pyx_v_popdfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_popstate); + __pyx_v_popstate = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 2); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_popnode); + __pyx_v_popnode = __pyx_3; + __pyx_3 = 0; + if (__Pyx_EndUnpack(__pyx_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":140 + * """Pop a nonterminal. (Internal)""" + * popdfa, popstate, popnode = self.stack.pop() + * newnode = self.convert(popnode) # <<<<<<<<<<<<<< + * if newnode is not None: + * if self.stack: + */ + __pyx_3 = ((struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser *)__pyx_v_self->__pyx_vtab)->convert(__pyx_v_self, __pyx_v_popnode); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 140; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_newnode); + __pyx_v_newnode = __pyx_3; + __pyx_3 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":141 + * popdfa, popstate, popnode = self.stack.pop() + * newnode = self.convert(popnode) + * if newnode is not None: # <<<<<<<<<<<<<< + * if self.stack: + * dfa, state, node = self.stack[-1] + */ + __pyx_4 = (__pyx_v_newnode != Py_None); + if (__pyx_4) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":142 + * newnode = self.convert(popnode) + * if newnode is not None: + * if self.stack: # <<<<<<<<<<<<<< + * dfa, state, node = self.stack[-1] + * node[-1].append(newnode) + */ + __pyx_4 = __Pyx_PyObject_IsTrue(__pyx_v_self->stack); if (unlikely(__pyx_4 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 142; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (__pyx_4) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":143 + * if newnode is not None: + * if self.stack: + * dfa, state, node = self.stack[-1] # <<<<<<<<<<<<<< + * node[-1].append(newnode) + * else: + */ + __pyx_2 = __Pyx_GetItemInt(__pyx_v_self->stack, -1, 0); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyTuple_CheckExact(__pyx_2) && PyTuple_GET_SIZE(__pyx_2) == 3) { + PyObject* tuple = __pyx_2; + __pyx_3 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_3); + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + Py_DECREF(__pyx_2); __pyx_2 = 0; + } + else { + __pyx_1 = PyObject_GetIter(__pyx_2); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 0); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_dfa); + __pyx_v_dfa = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_state); + __pyx_v_state = __pyx_3; + __pyx_3 = 0; + __pyx_3 = __Pyx_UnpackItem(__pyx_1, 2); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_node); + __pyx_v_node = __pyx_3; + __pyx_3 = 0; + if (__Pyx_EndUnpack(__pyx_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 143; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":144 + * if self.stack: + * dfa, state, node = self.stack[-1] + * node[-1].append(newnode) # <<<<<<<<<<<<<< + * else: + * self.rootnode = newnode + */ + __pyx_3 = __Pyx_GetItemInt(__pyx_v_node, -1, 0); if (!__pyx_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 144; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = __Pyx_PyObject_Append(__pyx_3, __pyx_v_newnode); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 144; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_3); __pyx_3 = 0; + Py_DECREF(__pyx_2); __pyx_2 = 0; + goto __pyx_L4; + } + /*else*/ { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":146 + * node[-1].append(newnode) + * else: + * self.rootnode = newnode # <<<<<<<<<<<<<< + * self.rootnode.used_names = self.used_names + * + */ + Py_INCREF(__pyx_v_newnode); + Py_DECREF(__pyx_v_self->rootnode); + __pyx_v_self->rootnode = __pyx_v_newnode; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":147 + * else: + * self.rootnode = newnode + * self.rootnode.used_names = self.used_names # <<<<<<<<<<<<<< + * + * cdef convert(self, raw_node): + */ + if (PyObject_SetAttr(__pyx_v_self->rootnode, __pyx_kp_used_names, __pyx_v_self->used_names) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 147; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + } + __pyx_L4:; + goto __pyx_L3; + } + __pyx_L3:; + + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + __Pyx_WriteUnraisable("sphinx.pycode.pgen2.parse.Parser.pop"); + __pyx_L0:; + Py_DECREF(__pyx_v_popdfa); + Py_DECREF(__pyx_v_popstate); + Py_DECREF(__pyx_v_popnode); + Py_DECREF(__pyx_v_newnode); + Py_DECREF(__pyx_v_dfa); + Py_DECREF(__pyx_v_state); + Py_DECREF(__pyx_v_node); +} + +/* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":149 + * self.rootnode.used_names = self.used_names + * + * cdef convert(self, raw_node): # <<<<<<<<<<<<<< + * type, value, context, children = raw_node + * if children or type in self._grammar_number2symbol: + */ + +static PyObject *__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_convert(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *__pyx_v_self, PyObject *__pyx_v_raw_node) { + PyObject *__pyx_v_type; + PyObject *__pyx_v_value; + PyObject *__pyx_v_context; + PyObject *__pyx_v_children; + PyObject *__pyx_r; + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + int __pyx_3; + Py_ssize_t __pyx_4 = 0; + PyObject *__pyx_5 = 0; + PyObject *__pyx_6 = 0; + __pyx_v_type = Py_None; Py_INCREF(Py_None); + __pyx_v_value = Py_None; Py_INCREF(Py_None); + __pyx_v_context = Py_None; Py_INCREF(Py_None); + __pyx_v_children = Py_None; Py_INCREF(Py_None); + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":150 + * + * cdef convert(self, raw_node): + * type, value, context, children = raw_node # <<<<<<<<<<<<<< + * if children or type in self._grammar_number2symbol: + * # If there's exactly one child, return that child instead of + */ + if (PyTuple_CheckExact(__pyx_v_raw_node) && PyTuple_GET_SIZE(__pyx_v_raw_node) == 4) { + PyObject* tuple = __pyx_v_raw_node; + __pyx_2 = PyTuple_GET_ITEM(tuple, 0); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_type); + __pyx_v_type = __pyx_2; + __pyx_2 = 0; + __pyx_2 = PyTuple_GET_ITEM(tuple, 1); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_value); + __pyx_v_value = __pyx_2; + __pyx_2 = 0; + __pyx_2 = PyTuple_GET_ITEM(tuple, 2); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_context); + __pyx_v_context = __pyx_2; + __pyx_2 = 0; + __pyx_2 = PyTuple_GET_ITEM(tuple, 3); + Py_INCREF(__pyx_2); + Py_DECREF(__pyx_v_children); + __pyx_v_children = __pyx_2; + __pyx_2 = 0; + } + else { + __pyx_1 = PyObject_GetIter(__pyx_v_raw_node); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = __Pyx_UnpackItem(__pyx_1, 0); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_type); + __pyx_v_type = __pyx_2; + __pyx_2 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_1, 1); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_value); + __pyx_v_value = __pyx_2; + __pyx_2 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_1, 2); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_context); + __pyx_v_context = __pyx_2; + __pyx_2 = 0; + __pyx_2 = __Pyx_UnpackItem(__pyx_1, 3); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_v_children); + __pyx_v_children = __pyx_2; + __pyx_2 = 0; + if (__Pyx_EndUnpack(__pyx_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + } + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":151 + * cdef convert(self, raw_node): + * type, value, context, children = raw_node + * if children or type in self._grammar_number2symbol: # <<<<<<<<<<<<<< + * # If there's exactly one child, return that child instead of + * # creating a new node. + */ + __pyx_2 = __pyx_v_children; + Py_INCREF(__pyx_2); + __pyx_3 = __Pyx_PyObject_IsTrue(__pyx_2); if (unlikely(__pyx_3 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 151; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (!__pyx_3) { + Py_DECREF(__pyx_2); __pyx_2 = 0; + __pyx_3 = (PySequence_Contains(__pyx_v_self->_grammar_number2symbol, __pyx_v_type)); if (unlikely(__pyx_3 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 151; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_2 = __Pyx_PyBool_FromLong(__pyx_3); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 151; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + } + __pyx_3 = __Pyx_PyObject_IsTrue(__pyx_2); if (unlikely(__pyx_3 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 151; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + if (__pyx_3) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":154 + * # If there's exactly one child, return that child instead of + * # creating a new node. + * if len(children) == 1: # <<<<<<<<<<<<<< + * return children[0] + * return Node(type, children, context=context) + */ + __pyx_4 = PyObject_Length(__pyx_v_children); if (unlikely(__pyx_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 154; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = (__pyx_4 == 1); + if (__pyx_3) { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":155 + * # creating a new node. + * if len(children) == 1: + * return children[0] # <<<<<<<<<<<<<< + * return Node(type, children, context=context) + * else: + */ + __pyx_1 = __Pyx_GetItemInt(__pyx_v_children, 0, 0); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 155; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_r = __pyx_1; + __pyx_1 = 0; + goto __pyx_L0; + goto __pyx_L4; + } + __pyx_L4:; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":156 + * if len(children) == 1: + * return children[0] + * return Node(type, children, context=context) # <<<<<<<<<<<<<< + * else: + * return Leaf(type, value, context=context) + */ + __pyx_2 = __Pyx_GetName(__pyx_m, __pyx_kp_Node); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_1 = PyTuple_New(2); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_1, 0, __pyx_v_type); + Py_INCREF(__pyx_v_children); + PyTuple_SET_ITEM(__pyx_1, 1, __pyx_v_children); + __pyx_5 = PyDict_New(); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyDict_SetItem(__pyx_5, __pyx_kp_context, __pyx_v_context) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_6 = PyEval_CallObjectWithKeywords(__pyx_2, ((PyObject *)__pyx_1), ((PyObject *)__pyx_5)); if (unlikely(!__pyx_6)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 156; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + Py_DECREF(((PyObject *)__pyx_1)); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_5)); __pyx_5 = 0; + __pyx_r = __pyx_6; + __pyx_6 = 0; + goto __pyx_L0; + goto __pyx_L3; + } + /*else*/ { + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":158 + * return Node(type, children, context=context) + * else: + * return Leaf(type, value, context=context) # <<<<<<<<<<<<<< + */ + __pyx_2 = __Pyx_GetName(__pyx_m, __pyx_kp_Leaf); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_1 = PyTuple_New(2); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_v_type); + PyTuple_SET_ITEM(__pyx_1, 0, __pyx_v_type); + Py_INCREF(__pyx_v_value); + PyTuple_SET_ITEM(__pyx_1, 1, __pyx_v_value); + __pyx_5 = PyDict_New(); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyDict_SetItem(__pyx_5, __pyx_kp_context, __pyx_v_context) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_6 = PyEval_CallObjectWithKeywords(__pyx_2, ((PyObject *)__pyx_1), ((PyObject *)__pyx_5)); if (unlikely(!__pyx_6)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_2); __pyx_2 = 0; + Py_DECREF(((PyObject *)__pyx_1)); __pyx_1 = 0; + Py_DECREF(((PyObject *)__pyx_5)); __pyx_5 = 0; + __pyx_r = __pyx_6; + __pyx_6 = 0; + goto __pyx_L0; + } + __pyx_L3:; + + __pyx_r = Py_None; Py_INCREF(Py_None); + goto __pyx_L0; + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_5); + Py_XDECREF(__pyx_6); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse.Parser.convert"); + __pyx_r = 0; + __pyx_L0:; + Py_DECREF(__pyx_v_type); + Py_DECREF(__pyx_v_value); + Py_DECREF(__pyx_v_context); + Py_DECREF(__pyx_v_children); + return __pyx_r; +} +static struct __pyx_vtabstruct_6sphinx_6pycode_5pgen2_5parse_Parser __pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser; + +static PyObject *__pyx_tp_new_6sphinx_6pycode_5pgen2_5parse_Parser(PyTypeObject *t, PyObject *a, PyObject *k) { + struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *p; + PyObject *o = (*t->tp_alloc)(t, 0); + if (!o) return 0; + p = ((struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)o); + p->__pyx_vtab = __pyx_vtabptr_6sphinx_6pycode_5pgen2_5parse_Parser; + p->grammar = Py_None; Py_INCREF(Py_None); + p->stack = Py_None; Py_INCREF(Py_None); + p->rootnode = Py_None; Py_INCREF(Py_None); + p->used_names = Py_None; Py_INCREF(Py_None); + p->_grammar_dfas = Py_None; Py_INCREF(Py_None); + p->_grammar_labels = Py_None; Py_INCREF(Py_None); + p->_grammar_keywords = Py_None; Py_INCREF(Py_None); + p->_grammar_tokens = Py_None; Py_INCREF(Py_None); + p->_grammar_number2symbol = Py_None; Py_INCREF(Py_None); + return o; +} + +static void __pyx_tp_dealloc_6sphinx_6pycode_5pgen2_5parse_Parser(PyObject *o) { + struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *p = (struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)o; + Py_XDECREF(p->grammar); + Py_XDECREF(p->stack); + Py_XDECREF(p->rootnode); + Py_XDECREF(p->used_names); + Py_XDECREF(p->_grammar_dfas); + Py_XDECREF(p->_grammar_labels); + Py_XDECREF(p->_grammar_keywords); + Py_XDECREF(p->_grammar_tokens); + Py_XDECREF(p->_grammar_number2symbol); + (*Py_TYPE(o)->tp_free)(o); +} + +static int __pyx_tp_traverse_6sphinx_6pycode_5pgen2_5parse_Parser(PyObject *o, visitproc v, void *a) { + int e; + struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *p = (struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)o; + if (p->grammar) { + e = (*v)(p->grammar, a); if (e) return e; + } + if (p->stack) { + e = (*v)(p->stack, a); if (e) return e; + } + if (p->rootnode) { + e = (*v)(p->rootnode, a); if (e) return e; + } + if (p->used_names) { + e = (*v)(p->used_names, a); if (e) return e; + } + if (p->_grammar_dfas) { + e = (*v)(p->_grammar_dfas, a); if (e) return e; + } + if (p->_grammar_labels) { + e = (*v)(p->_grammar_labels, a); if (e) return e; + } + if (p->_grammar_keywords) { + e = (*v)(p->_grammar_keywords, a); if (e) return e; + } + if (p->_grammar_tokens) { + e = (*v)(p->_grammar_tokens, a); if (e) return e; + } + if (p->_grammar_number2symbol) { + e = (*v)(p->_grammar_number2symbol, a); if (e) return e; + } + return 0; +} + +static int __pyx_tp_clear_6sphinx_6pycode_5pgen2_5parse_Parser(PyObject *o) { + struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *p = (struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser *)o; + PyObject* tmp; + tmp = ((PyObject*)p->grammar); + p->grammar = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->stack); + p->stack = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->rootnode); + p->rootnode = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->used_names); + p->used_names = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->_grammar_dfas); + p->_grammar_dfas = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->_grammar_labels); + p->_grammar_labels = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->_grammar_keywords); + p->_grammar_keywords = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->_grammar_tokens); + p->_grammar_tokens = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + tmp = ((PyObject*)p->_grammar_number2symbol); + p->_grammar_number2symbol = Py_None; Py_INCREF(Py_None); + Py_XDECREF(tmp); + return 0; +} + +static struct PyMethodDef __pyx_methods_6sphinx_6pycode_5pgen2_5parse_Parser[] = { + {"setup", (PyCFunction)__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_setup, METH_VARARGS|METH_KEYWORDS, 0}, + {"addtoken", (PyCFunction)__pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser_addtoken, METH_VARARGS|METH_KEYWORDS, __pyx_doc_6sphinx_6pycode_5pgen2_5parse_6Parser_addtoken}, + {0, 0, 0, 0} +}; + +static struct PyMemberDef __pyx_members_6sphinx_6pycode_5pgen2_5parse_Parser[] = { + {"grammar", T_OBJECT, offsetof(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser, grammar), 0, 0}, + {"stack", T_OBJECT, offsetof(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser, stack), 0, 0}, + {"rootnode", T_OBJECT, offsetof(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser, rootnode), 0, 0}, + {"used_names", T_OBJECT, offsetof(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser, used_names), 0, 0}, + {0, 0, 0, 0, 0} +}; + +static PyNumberMethods __pyx_tp_as_number_Parser = { + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + #if PY_MAJOR_VERSION < 3 + 0, /*nb_divide*/ + #endif + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + 0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + #if PY_MAJOR_VERSION < 3 + 0, /*nb_coerce*/ + #endif + 0, /*nb_int*/ + 0, /*nb_long*/ + 0, /*nb_float*/ + #if PY_MAJOR_VERSION < 3 + 0, /*nb_oct*/ + #endif + #if PY_MAJOR_VERSION < 3 + 0, /*nb_hex*/ + #endif + 0, /*nb_inplace_add*/ + 0, /*nb_inplace_subtract*/ + 0, /*nb_inplace_multiply*/ + #if PY_MAJOR_VERSION < 3 + 0, /*nb_inplace_divide*/ + #endif + 0, /*nb_inplace_remainder*/ + 0, /*nb_inplace_power*/ + 0, /*nb_inplace_lshift*/ + 0, /*nb_inplace_rshift*/ + 0, /*nb_inplace_and*/ + 0, /*nb_inplace_xor*/ + 0, /*nb_inplace_or*/ + 0, /*nb_floor_divide*/ + 0, /*nb_true_divide*/ + 0, /*nb_inplace_floor_divide*/ + 0, /*nb_inplace_true_divide*/ + #if (PY_MAJOR_VERSION >= 3) || (Py_TPFLAGS_DEFAULT & Py_TPFLAGS_HAVE_INDEX) + 0, /*nb_index*/ + #endif +}; + +static PySequenceMethods __pyx_tp_as_sequence_Parser = { + 0, /*sq_length*/ + 0, /*sq_concat*/ + 0, /*sq_repeat*/ + 0, /*sq_item*/ + 0, /*sq_slice*/ + 0, /*sq_ass_item*/ + 0, /*sq_ass_slice*/ + 0, /*sq_contains*/ + 0, /*sq_inplace_concat*/ + 0, /*sq_inplace_repeat*/ +}; + +static PyMappingMethods __pyx_tp_as_mapping_Parser = { + 0, /*mp_length*/ + 0, /*mp_subscript*/ + 0, /*mp_ass_subscript*/ +}; + +static PyBufferProcs __pyx_tp_as_buffer_Parser = { + #if PY_MAJOR_VERSION < 3 + 0, /*bf_getreadbuffer*/ + #endif + #if PY_MAJOR_VERSION < 3 + 0, /*bf_getwritebuffer*/ + #endif + #if PY_MAJOR_VERSION < 3 + 0, /*bf_getsegcount*/ + #endif + #if PY_MAJOR_VERSION < 3 + 0, /*bf_getcharbuffer*/ + #endif + #if (PY_MAJOR_VERSION >= 3) || (Py_TPFLAGS_DEFAULT & Py_TPFLAGS_HAVE_NEWBUFFER) + 0, /*bf_getbuffer*/ + #endif + #if (PY_MAJOR_VERSION >= 3) || (Py_TPFLAGS_DEFAULT & Py_TPFLAGS_HAVE_NEWBUFFER) + 0, /*bf_releasebuffer*/ + #endif +}; + +PyTypeObject __pyx_type_6sphinx_6pycode_5pgen2_5parse_Parser = { + PyVarObject_HEAD_INIT(0, 0) + "sphinx.pycode.pgen2.parse.Parser", /*tp_name*/ + sizeof(struct __pyx_obj_6sphinx_6pycode_5pgen2_5parse_Parser), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + __pyx_tp_dealloc_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + &__pyx_tp_as_number_Parser, /*tp_as_number*/ + &__pyx_tp_as_sequence_Parser, /*tp_as_sequence*/ + &__pyx_tp_as_mapping_Parser, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + &__pyx_tp_as_buffer_Parser, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + 0, /*tp_doc*/ + __pyx_tp_traverse_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_traverse*/ + __pyx_tp_clear_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + __pyx_methods_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_methods*/ + __pyx_members_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + __pyx_pf_6sphinx_6pycode_5pgen2_5parse_6Parser___init__, /*tp_init*/ + 0, /*tp_alloc*/ + __pyx_tp_new_6sphinx_6pycode_5pgen2_5parse_Parser, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ + 0, /*tp_bases*/ + 0, /*tp_mro*/ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0, /*tp_weaklist*/ +}; + +static struct PyMethodDef __pyx_methods[] = { + {0, 0, 0, 0} +}; + +static void __pyx_init_filenames(void); /*proto*/ + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef __pyx_moduledef = { + PyModuleDef_HEAD_INIT, + "parse", + 0, /* m_doc */ + -1, /* m_size */ + __pyx_methods /* m_methods */, + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL /* m_free */ +}; +#endif + +static __Pyx_StringTabEntry __pyx_string_tab[] = { + {&__pyx_kp___init__, __pyx_k___init__, sizeof(__pyx_k___init__), 0, 1, 1}, + {&__pyx_kp_setup, __pyx_k_setup, sizeof(__pyx_k_setup), 0, 1, 1}, + {&__pyx_kp_addtoken, __pyx_k_addtoken, sizeof(__pyx_k_addtoken), 0, 1, 1}, + {&__pyx_kp_1, __pyx_k_1, sizeof(__pyx_k_1), 1, 1, 1}, + {&__pyx_kp_Node, __pyx_k_Node, sizeof(__pyx_k_Node), 1, 1, 1}, + {&__pyx_kp_Leaf, __pyx_k_Leaf, sizeof(__pyx_k_Leaf), 1, 1, 1}, + {&__pyx_kp_ParseError, __pyx_k_ParseError, sizeof(__pyx_k_ParseError), 0, 1, 1}, + {&__pyx_kp_Exception, __pyx_k_Exception, sizeof(__pyx_k_Exception), 1, 1, 1}, + {&__pyx_kp_msg, __pyx_k_msg, sizeof(__pyx_k_msg), 1, 1, 1}, + {&__pyx_kp_type, __pyx_k_type, sizeof(__pyx_k_type), 1, 1, 1}, + {&__pyx_kp_value, __pyx_k_value, sizeof(__pyx_k_value), 1, 1, 1}, + {&__pyx_kp_context, __pyx_k_context, sizeof(__pyx_k_context), 1, 1, 1}, + {&__pyx_kp_dfas, __pyx_k_dfas, sizeof(__pyx_k_dfas), 1, 1, 1}, + {&__pyx_kp_labels, __pyx_k_labels, sizeof(__pyx_k_labels), 1, 1, 1}, + {&__pyx_kp_keywords, __pyx_k_keywords, sizeof(__pyx_k_keywords), 1, 1, 1}, + {&__pyx_kp_tokens, __pyx_k_tokens, sizeof(__pyx_k_tokens), 1, 1, 1}, + {&__pyx_kp_4, __pyx_k_4, sizeof(__pyx_k_4), 1, 1, 1}, + {&__pyx_kp_start, __pyx_k_start, sizeof(__pyx_k_start), 1, 1, 1}, + {&__pyx_kp_add, __pyx_k_add, sizeof(__pyx_k_add), 1, 1, 1}, + {&__pyx_kp_get, __pyx_k_get, sizeof(__pyx_k_get), 1, 1, 1}, + {&__pyx_kp_append, __pyx_k_append, sizeof(__pyx_k_append), 1, 1, 1}, + {&__pyx_kp_pop, __pyx_k_pop, sizeof(__pyx_k_pop), 1, 1, 1}, + {&__pyx_kp_used_names, __pyx_k_used_names, sizeof(__pyx_k_used_names), 1, 1, 1}, + {&__pyx_kp_2, __pyx_k_2, sizeof(__pyx_k_2), 0, 0, 0}, + {&__pyx_kp_3, __pyx_k_3, sizeof(__pyx_k_3), 0, 0, 0}, + {&__pyx_kp_5, __pyx_k_5, sizeof(__pyx_k_5), 0, 0, 0}, + {&__pyx_kp_6, __pyx_k_6, sizeof(__pyx_k_6), 0, 0, 0}, + {&__pyx_kp_7, __pyx_k_7, sizeof(__pyx_k_7), 0, 0, 0}, + {0, 0, 0, 0, 0, 0} +}; +static int __Pyx_InitCachedBuiltins(void) { + __pyx_builtin_Exception = __Pyx_GetName(__pyx_b, __pyx_kp_Exception); if (!__pyx_builtin_Exception) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + return 0; + __pyx_L1_error:; + return -1; +} + +static int __Pyx_InitGlobals(void) { + __pyx_int_0 = PyInt_FromLong(0); if (unlikely(!__pyx_int_0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + return 0; + __pyx_L1_error:; + return -1; +} + +#if PY_MAJOR_VERSION < 3 +PyMODINIT_FUNC initparse(void); /*proto*/ +PyMODINIT_FUNC initparse(void) +#else +PyMODINIT_FUNC PyInit_parse(void); /*proto*/ +PyMODINIT_FUNC PyInit_parse(void) +#endif +{ + PyObject *__pyx_1 = 0; + PyObject *__pyx_2 = 0; + PyObject *__pyx_3 = 0; + PyObject *__pyx_4 = 0; + __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + /*--- Libary function declarations ---*/ + __pyx_init_filenames(); + /*--- Initialize various global constants etc. ---*/ + if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + /*--- Module creation code ---*/ + #if PY_MAJOR_VERSION < 3 + __pyx_m = Py_InitModule4("parse", __pyx_methods, 0, 0, PYTHON_API_VERSION); + #else + __pyx_m = PyModule_Create(&__pyx_moduledef); + #endif + if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + #if PY_MAJOR_VERSION < 3 + Py_INCREF(__pyx_m); + #endif + __pyx_b = PyImport_AddModule(__Pyx_BUILTIN_MODULE_NAME); + if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; + /*--- Builtin init code ---*/ + if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_skip_dispatch = 0; + /*--- Global init code ---*/ + /*--- Function export code ---*/ + /*--- Type init code ---*/ + __pyx_vtabptr_6sphinx_6pycode_5pgen2_5parse_Parser = &__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser; + *(void(**)(void))&__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser.classify = (void(*)(void))__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_classify; + *(void(**)(void))&__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser.shift = (void(*)(void))__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_shift; + *(void(**)(void))&__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser.push = (void(*)(void))__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_push; + *(void(**)(void))&__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser.pop = (void(*)(void))__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_pop; + *(void(**)(void))&__pyx_vtable_6sphinx_6pycode_5pgen2_5parse_Parser.convert = (void(*)(void))__pyx_f_6sphinx_6pycode_5pgen2_5parse_6Parser_convert; + if (PyType_Ready(&__pyx_type_6sphinx_6pycode_5pgen2_5parse_Parser) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (__Pyx_SetVtable(__pyx_type_6sphinx_6pycode_5pgen2_5parse_Parser.tp_dict, __pyx_vtabptr_6sphinx_6pycode_5pgen2_5parse_Parser) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyObject_SetAttrString(__pyx_m, "Parser", (PyObject *)&__pyx_type_6sphinx_6pycode_5pgen2_5parse_Parser) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_ptype_6sphinx_6pycode_5pgen2_5parse_Parser = &__pyx_type_6sphinx_6pycode_5pgen2_5parse_Parser; + /*--- Type import code ---*/ + /*--- Function import code ---*/ + /*--- Execution code ---*/ + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":15 + * """ + * + * from sphinx.pycode.nodes import Node, Leaf # <<<<<<<<<<<<<< + * + * DEF NAME = 1 + */ + __pyx_1 = PyList_New(2); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_kp_Node); + PyList_SET_ITEM(__pyx_1, 0, __pyx_kp_Node); + Py_INCREF(__pyx_kp_Leaf); + PyList_SET_ITEM(__pyx_1, 1, __pyx_kp_Leaf); + __pyx_2 = __Pyx_Import(__pyx_kp_1, ((PyObject *)__pyx_1)); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_1)); __pyx_1 = 0; + __pyx_1 = PyObject_GetAttr(__pyx_2, __pyx_kp_Node); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyObject_SetAttr(__pyx_m, __pyx_kp_Node, __pyx_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + __pyx_1 = PyObject_GetAttr(__pyx_2, __pyx_kp_Leaf); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + if (PyObject_SetAttr(__pyx_m, __pyx_kp_Leaf, __pyx_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + Py_DECREF(__pyx_2); __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":19 + * DEF NAME = 1 + * + * class ParseError(Exception): # <<<<<<<<<<<<<< + * """Exception to signal the parser is stuck.""" + * + */ + __pyx_2 = PyDict_New(); if (unlikely(!__pyx_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_1 = PyTuple_New(1); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_INCREF(__pyx_builtin_Exception); + PyTuple_SET_ITEM(__pyx_1, 0, __pyx_builtin_Exception); + if (PyDict_SetItemString(((PyObject *)__pyx_2), "__doc__", __pyx_kp_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_3 = __Pyx_CreateClass(((PyObject *)__pyx_1), ((PyObject *)__pyx_2), __pyx_kp_ParseError, "sphinx.pycode.pgen2.parse"); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(((PyObject *)__pyx_1)); __pyx_1 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":22 + * """Exception to signal the parser is stuck.""" + * + * def __init__(self, msg, type, value, context): # <<<<<<<<<<<<<< + * Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + * (msg, type, value, context)) + */ + __pyx_1 = PyCFunction_New(&__pyx_mdef_6sphinx_6pycode_5pgen2_5parse_10ParseError___init__, 0); if (unlikely(!__pyx_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + __pyx_4 = PyMethod_New(__pyx_1, 0, __pyx_3); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_1); __pyx_1 = 0; + if (PyObject_SetAttr(__pyx_3, __pyx_kp___init__, __pyx_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 22; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_4); __pyx_4 = 0; + if (PyObject_SetAttr(__pyx_m, __pyx_kp_ParseError, __pyx_3) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;} + Py_DECREF(__pyx_3); __pyx_3 = 0; + Py_DECREF(((PyObject *)__pyx_2)); __pyx_2 = 0; + + /* "/home/gbr/devel/sphinx/sphinx/pycode/pgen2/parse.pyx":149 + * self.rootnode.used_names = self.used_names + * + * cdef convert(self, raw_node): # <<<<<<<<<<<<<< + * type, value, context, children = raw_node + * if children or type in self._grammar_number2symbol: + */ + #if PY_MAJOR_VERSION < 3 + return; + #else + return __pyx_m; + #endif + __pyx_L1_error:; + Py_XDECREF(__pyx_1); + Py_XDECREF(__pyx_2); + Py_XDECREF(__pyx_3); + Py_XDECREF(__pyx_4); + __Pyx_AddTraceback("sphinx.pycode.pgen2.parse"); + #if PY_MAJOR_VERSION >= 3 + return NULL; + #endif +} + +static const char *__pyx_filenames[] = { + "parse.pyx", +}; + +/* Runtime support code */ + +static void __pyx_init_filenames(void) { + __pyx_f = __pyx_filenames; +} + +static INLINE void __Pyx_RaiseArgtupleTooLong( + Py_ssize_t num_expected, + Py_ssize_t num_found) +{ + const char* error_message = + #if PY_VERSION_HEX < 0x02050000 + "function takes at most %d positional arguments (%d given)"; + #else + "function takes at most %zd positional arguments (%zd given)"; + #endif + PyErr_Format(PyExc_TypeError, error_message, num_expected, num_found); +} + +static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) { + PyObject *__import__ = 0; + PyObject *empty_list = 0; + PyObject *module = 0; + PyObject *global_dict = 0; + PyObject *empty_dict = 0; + PyObject *list; + __import__ = PyObject_GetAttrString(__pyx_b, "__import__"); + if (!__import__) + goto bad; + if (from_list) + list = from_list; + else { + empty_list = PyList_New(0); + if (!empty_list) + goto bad; + list = empty_list; + } + global_dict = PyModule_GetDict(__pyx_m); + if (!global_dict) + goto bad; + empty_dict = PyDict_New(); + if (!empty_dict) + goto bad; + module = PyObject_CallFunction(__import__, "OOOO", + name, global_dict, empty_dict, list); +bad: + Py_XDECREF(empty_list); + Py_XDECREF(__import__); + Py_XDECREF(empty_dict); + return module; +} + +static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) { + PyObject *result; + result = PyObject_GetAttr(dict, name); + if (!result) + PyErr_SetObject(PyExc_NameError, name); + return result; +} + +static PyObject *__Pyx_CreateClass( + PyObject *bases, PyObject *dict, PyObject *name, char *modname) +{ + PyObject *py_modname; + PyObject *result = 0; + + #if PY_MAJOR_VERSION < 3 + py_modname = PyString_FromString(modname); + #else + py_modname = PyUnicode_FromString(modname); + #endif + if (!py_modname) + goto bad; + if (PyDict_SetItemString(dict, "__module__", py_modname) < 0) + goto bad; + #if PY_MAJOR_VERSION < 3 + result = PyClass_New(bases, dict, name); + #else + result = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, name, bases, dict, NULL); + #endif +bad: + Py_XDECREF(py_modname); + return result; +} + + +static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) { + PyObject *item; + if (!(item = PyIter_Next(iter))) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, + #if PY_VERSION_HEX < 0x02050000 + "need more than %d values to unpack", (int)index); + #else + "need more than %zd values to unpack", index); + #endif + } + } + return item; +} + +static int __Pyx_EndUnpack(PyObject *iter) { + PyObject *item; + if ((item = PyIter_Next(iter))) { + Py_DECREF(item); + PyErr_SetString(PyExc_ValueError, "too many values to unpack"); + return -1; + } + else if (!PyErr_Occurred()) + return 0; + else + return -1; +} + +static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) { + Py_XINCREF(type); + Py_XINCREF(value); + Py_XINCREF(tb); + /* First, check the traceback argument, replacing None with NULL. */ + if (tb == Py_None) { + Py_DECREF(tb); + tb = 0; + } + else if (tb != NULL && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "raise: arg 3 must be a traceback or None"); + goto raise_error; + } + /* Next, replace a missing value with None */ + if (value == NULL) { + value = Py_None; + Py_INCREF(value); + } + #if PY_VERSION_HEX < 0x02050000 + if (!PyClass_Check(type)) + #else + if (!PyType_Check(type)) + #endif + { + /* Raising an instance. The value should be a dummy. */ + if (value != Py_None) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto raise_error; + } + /* Normalize to raise <class>, <instance> */ + Py_DECREF(value); + value = type; + #if PY_VERSION_HEX < 0x02050000 + if (PyInstance_Check(type)) { + type = (PyObject*) ((PyInstanceObject*)type)->in_class; + Py_INCREF(type); + } + else { + type = 0; + PyErr_SetString(PyExc_TypeError, + "raise: exception must be an old-style class or instance"); + goto raise_error; + } + #else + type = (PyObject*) Py_TYPE(type); + Py_INCREF(type); + if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { + PyErr_SetString(PyExc_TypeError, + "raise: exception class must be a subclass of BaseException"); + goto raise_error; + } + #endif + } + PyErr_Restore(type, value, tb); + return; +raise_error: + Py_XDECREF(value); + Py_XDECREF(type); + Py_XDECREF(tb); + return; +} + + +static void __Pyx_WriteUnraisable(const char *name) { + PyObject *old_exc, *old_val, *old_tb; + PyObject *ctx; + PyErr_Fetch(&old_exc, &old_val, &old_tb); + #if PY_MAJOR_VERSION < 3 + ctx = PyString_FromString(name); + #else + ctx = PyUnicode_FromString(name); + #endif + PyErr_Restore(old_exc, old_val, old_tb); + if (!ctx) + ctx = Py_None; + PyErr_WriteUnraisable(ctx); +} + +static int __Pyx_SetVtable(PyObject *dict, void *vtable) { + PyObject *pycobj = 0; + int result; + + pycobj = PyCObject_FromVoidPtr(vtable, 0); + if (!pycobj) + goto bad; + if (PyDict_SetItemString(dict, "__pyx_vtable__", pycobj) < 0) + goto bad; + result = 0; + goto done; + +bad: + result = -1; +done: + Py_XDECREF(pycobj); + return result; +} + +#include "compile.h" +#include "frameobject.h" +#include "traceback.h" + +static void __Pyx_AddTraceback(const char *funcname) { + PyObject *py_srcfile = 0; + PyObject *py_funcname = 0; + PyObject *py_globals = 0; + PyObject *empty_string = 0; + PyCodeObject *py_code = 0; + PyFrameObject *py_frame = 0; + + #if PY_MAJOR_VERSION < 3 + py_srcfile = PyString_FromString(__pyx_filename); + #else + py_srcfile = PyUnicode_FromString(__pyx_filename); + #endif + if (!py_srcfile) goto bad; + if (__pyx_clineno) { + #if PY_MAJOR_VERSION < 3 + py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); + #else + py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); + #endif + } + else { + #if PY_MAJOR_VERSION < 3 + py_funcname = PyString_FromString(funcname); + #else + py_funcname = PyUnicode_FromString(funcname); + #endif + } + if (!py_funcname) goto bad; + py_globals = PyModule_GetDict(__pyx_m); + if (!py_globals) goto bad; + #if PY_MAJOR_VERSION < 3 + empty_string = PyString_FromStringAndSize("", 0); + #else + empty_string = PyBytes_FromStringAndSize("", 0); + #endif + if (!empty_string) goto bad; + py_code = PyCode_New( + 0, /*int argcount,*/ + #if PY_MAJOR_VERSION >= 3 + 0, /*int kwonlyargcount,*/ + #endif + 0, /*int nlocals,*/ + 0, /*int stacksize,*/ + 0, /*int flags,*/ + empty_string, /*PyObject *code,*/ + __pyx_empty_tuple, /*PyObject *consts,*/ + __pyx_empty_tuple, /*PyObject *names,*/ + __pyx_empty_tuple, /*PyObject *varnames,*/ + __pyx_empty_tuple, /*PyObject *freevars,*/ + __pyx_empty_tuple, /*PyObject *cellvars,*/ + py_srcfile, /*PyObject *filename,*/ + py_funcname, /*PyObject *name,*/ + __pyx_lineno, /*int firstlineno,*/ + empty_string /*PyObject *lnotab*/ + ); + if (!py_code) goto bad; + py_frame = PyFrame_New( + PyThreadState_Get(), /*PyThreadState *tstate,*/ + py_code, /*PyCodeObject *code,*/ + py_globals, /*PyObject *globals,*/ + 0 /*PyObject *locals*/ + ); + if (!py_frame) goto bad; + py_frame->f_lineno = __pyx_lineno; + PyTraceBack_Here(py_frame); +bad: + Py_XDECREF(py_srcfile); + Py_XDECREF(py_funcname); + Py_XDECREF(empty_string); + Py_XDECREF(py_code); + Py_XDECREF(py_frame); +} + +static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { + while (t->p) { + #if PY_MAJOR_VERSION < 3 + if (t->is_unicode && (!t->is_identifier)) { + *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); + } else if (t->intern) { + *t->p = PyString_InternFromString(t->s); + } else { + *t->p = PyString_FromStringAndSize(t->s, t->n - 1); + } + #else /* Python 3+ has unicode identifiers */ + if (t->is_identifier || (t->is_unicode && t->intern)) { + *t->p = PyUnicode_InternFromString(t->s); + } else if (t->is_unicode) { + *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1); + } else { + *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1); + } + #endif + if (!*t->p) + return -1; + ++t; + } + return 0; +} + +/* Type Conversion Functions */ + +static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b) { + Py_ssize_t ival; + PyObject* x = PyNumber_Index(b); + if (!x) return -1; + ival = PyInt_AsSsize_t(x); + Py_DECREF(x); + return ival; +} + +static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { + if (x == Py_True) return 1; + else if (x == Py_False) return 0; + else return PyObject_IsTrue(x); +} + +static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x) { + if (PyInt_CheckExact(x)) { + return PyInt_AS_LONG(x); + } + else if (PyLong_CheckExact(x)) { + return PyLong_AsLongLong(x); + } + else { + PY_LONG_LONG val; + PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; + val = __pyx_PyInt_AsLongLong(tmp); + Py_DECREF(tmp); + return val; + } +} + +static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x) { + if (PyInt_CheckExact(x)) { + long val = PyInt_AS_LONG(x); + if (unlikely(val < 0)) { + PyErr_SetString(PyExc_TypeError, "Negative assignment to unsigned type."); + return (unsigned PY_LONG_LONG)-1; + } + return val; + } + else if (PyLong_CheckExact(x)) { + return PyLong_AsUnsignedLongLong(x); + } + else { + PY_LONG_LONG val; + PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; + val = __pyx_PyInt_AsUnsignedLongLong(tmp); + Py_DECREF(tmp); + return val; + } +} + + +static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x) { + if (sizeof(unsigned char) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + unsigned char val = (unsigned char)long_val; + if (unlikely((val != long_val) || (long_val < 0))) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned char"); + return (unsigned char)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x) { + if (sizeof(unsigned short) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + unsigned short val = (unsigned short)long_val; + if (unlikely((val != long_val) || (long_val < 0))) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned short"); + return (unsigned short)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE char __pyx_PyInt_char(PyObject* x) { + if (sizeof(char) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + char val = (char)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to char"); + return (char)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE short __pyx_PyInt_short(PyObject* x) { + if (sizeof(short) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + short val = (short)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to short"); + return (short)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE int __pyx_PyInt_int(PyObject* x) { + if (sizeof(int) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + int val = (int)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to int"); + return (int)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE long __pyx_PyInt_long(PyObject* x) { + if (sizeof(long) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + long val = (long)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to long"); + return (long)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE signed char __pyx_PyInt_signed_char(PyObject* x) { + if (sizeof(signed char) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + signed char val = (signed char)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed char"); + return (signed char)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE signed short __pyx_PyInt_signed_short(PyObject* x) { + if (sizeof(signed short) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + signed short val = (signed short)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed short"); + return (signed short)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE signed int __pyx_PyInt_signed_int(PyObject* x) { + if (sizeof(signed int) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + signed int val = (signed int)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed int"); + return (signed int)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE signed long __pyx_PyInt_signed_long(PyObject* x) { + if (sizeof(signed long) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + signed long val = (signed long)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed long"); + return (signed long)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + +static INLINE long double __pyx_PyInt_long_double(PyObject* x) { + if (sizeof(long double) < sizeof(long)) { + long long_val = __pyx_PyInt_AsLong(x); + long double val = (long double)long_val; + if (unlikely((val != long_val) )) { + PyErr_SetString(PyExc_OverflowError, "value too large to convert to long double"); + return (long double)-1; + } + return val; + } + else { + return __pyx_PyInt_AsLong(x); + } +} + diff --git a/sphinx/pycode/pgen2/parse.py b/sphinx/pycode/pgen2/parse.py new file mode 100644 index 00000000..60eec05e --- /dev/null +++ b/sphinx/pycode/pgen2/parse.py @@ -0,0 +1,201 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Parser engine for the grammar tables generated by pgen. + +The grammar table must be loaded first. + +See Parser/parser.c in the Python distribution for additional info on +how this parsing engine works. + +""" + +# Local imports +from sphinx.pycode.pgen2 import token + +class ParseError(Exception): + """Exception to signal the parser is stuck.""" + + def __init__(self, msg, type, value, context): + Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + (msg, type, value, context)) + self.msg = msg + self.type = type + self.value = value + self.context = context + +class Parser(object): + """Parser engine. + + The proper usage sequence is: + + p = Parser(grammar, [converter]) # create instance + p.setup([start]) # prepare for parsing + <for each input token>: + if p.addtoken(...): # parse a token; may raise ParseError + break + root = p.rootnode # root of abstract syntax tree + + A Parser instance may be reused by calling setup() repeatedly. + + A Parser instance contains state pertaining to the current token + sequence, and should not be used concurrently by different threads + to parse separate token sequences. + + See driver.py for how to get input tokens by tokenizing a file or + string. + + Parsing is complete when addtoken() returns True; the root of the + abstract syntax tree can then be retrieved from the rootnode + instance variable. When a syntax error occurs, addtoken() raises + the ParseError exception. There is no error recovery; the parser + cannot be used after a syntax error was reported (but it can be + reinitialized by calling setup()). + + """ + + def __init__(self, grammar, convert=None): + """Constructor. + + The grammar argument is a grammar.Grammar instance; see the + grammar module for more information. + + The parser is not ready yet for parsing; you must call the + setup() method to get it started. + + The optional convert argument is a function mapping concrete + syntax tree nodes to abstract syntax tree nodes. If not + given, no conversion is done and the syntax tree produced is + the concrete syntax tree. If given, it must be a function of + two arguments, the first being the grammar (a grammar.Grammar + instance), and the second being the concrete syntax tree node + to be converted. The syntax tree is converted from the bottom + up. + + A concrete syntax tree node is a (type, value, context, nodes) + tuple, where type is the node type (a token or symbol number), + value is None for symbols and a string for tokens, context is + None or an opaque value used for error reporting (typically a + (lineno, offset) pair), and nodes is a list of children for + symbols, and None for tokens. + + An abstract syntax tree node may be anything; this is entirely + up to the converter function. + + """ + self.grammar = grammar + self.convert = convert or (lambda grammar, node: node) + + def setup(self, start=None): + """Prepare for parsing. + + This *must* be called before starting to parse. + + The optional argument is an alternative start symbol; it + defaults to the grammar's start symbol. + + You can use a Parser instance to parse any number of programs; + each time you call setup() the parser is reset to an initial + state determined by the (implicit or explicit) start symbol. + + """ + if start is None: + start = self.grammar.start + # Each stack entry is a tuple: (dfa, state, node). + # A node is a tuple: (type, value, context, children), + # where children is a list of nodes or None, and context may be None. + newnode = (start, None, None, []) + stackentry = (self.grammar.dfas[start], 0, newnode) + self.stack = [stackentry] + self.rootnode = None + self.used_names = set() # Aliased to self.rootnode.used_names in pop() + + def addtoken(self, type, value, context): + """Add a token; return True iff this is the end of the program.""" + # Map from token to label + ilabel = self.classify(type, value, context) + # Loop until the token is shifted; may raise exceptions + while True: + dfa, state, node = self.stack[-1] + states, first = dfa + arcs = states[state] + # Look for a state with this label + for i, newstate in arcs: + t, v = self.grammar.labels[i] + if ilabel == i: + # Look it up in the list of labels + assert t < 256 + # Shift a token; we're done with it + self.shift(type, value, newstate, context) + # Pop while we are in an accept-only state + state = newstate + while states[state] == [(0, state)]: + self.pop() + if not self.stack: + # Done parsing! + return True + dfa, state, node = self.stack[-1] + states, first = dfa + # Done with this token + return False + elif t >= 256: + # See if it's a symbol and if we're in its first set + itsdfa = self.grammar.dfas[t] + itsstates, itsfirst = itsdfa + if ilabel in itsfirst: + # Push a symbol + self.push(t, self.grammar.dfas[t], newstate, context) + break # To continue the outer while loop + else: + if (0, state) in arcs: + # An accepting state, pop it and try something else + self.pop() + if not self.stack: + # Done parsing, but another token is input + raise ParseError("too much input", + type, value, context) + else: + # No success finding a transition + raise ParseError("bad input", type, value, context) + + def classify(self, type, value, context): + """Turn a token into a label. (Internal)""" + if type == token.NAME: + # Keep a listing of all used names + self.used_names.add(value) + # Check for reserved words + ilabel = self.grammar.keywords.get(value) + if ilabel is not None: + return ilabel + ilabel = self.grammar.tokens.get(type) + if ilabel is None: + raise ParseError("bad token", type, value, context) + return ilabel + + def shift(self, type, value, newstate, context): + """Shift a token. (Internal)""" + dfa, state, node = self.stack[-1] + newnode = (type, value, context, None) + newnode = self.convert(self.grammar, newnode) + if newnode is not None: + node[-1].append(newnode) + self.stack[-1] = (dfa, newstate, node) + + def push(self, type, newdfa, newstate, context): + """Push a nonterminal. (Internal)""" + dfa, state, node = self.stack[-1] + newnode = (type, None, context, []) + self.stack[-1] = (dfa, newstate, node) + self.stack.append((newdfa, 0, newnode)) + + def pop(self): + """Pop a nonterminal. (Internal)""" + popdfa, popstate, popnode = self.stack.pop() + newnode = self.convert(self.grammar, popnode) + if newnode is not None: + if self.stack: + dfa, state, node = self.stack[-1] + node[-1].append(newnode) + else: + self.rootnode = newnode + self.rootnode.used_names = self.used_names diff --git a/sphinx/pycode/pgen2/parse.pyx b/sphinx/pycode/pgen2/parse.pyx new file mode 100644 index 00000000..537d7393 --- /dev/null +++ b/sphinx/pycode/pgen2/parse.pyx @@ -0,0 +1,158 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Adapted from parse.py to be compiled with Cython by Georg Brandl. + +"""Parser engine for the grammar tables generated by pgen. + +The grammar table must be loaded first. + +See Parser/parser.c in the Python distribution for additional info on +how this parsing engine works. + +""" + +from sphinx.pycode.nodes import Node, Leaf + +DEF NAME = 1 + +class ParseError(Exception): + """Exception to signal the parser is stuck.""" + + def __init__(self, msg, type, value, context): + Exception.__init__(self, "%s: type=%r, value=%r, context=%r" % + (msg, type, value, context)) + self.msg = msg + self.type = type + self.value = value + self.context = context + + +cdef class Parser: + cdef public grammar, stack, rootnode, used_names + cdef _grammar_dfas, _grammar_labels, _grammar_keywords, _grammar_tokens + cdef _grammar_number2symbol + + def __init__(self, grammar, convert=None): + self.grammar = grammar + #self.convert = convert or noconvert + + self._grammar_dfas = grammar.dfas + self._grammar_labels = grammar.labels + self._grammar_keywords = grammar.keywords + self._grammar_tokens = grammar.tokens + self._grammar_number2symbol = grammar.number2symbol + + def setup(self, start=None): + if start is None: + start = self.grammar.start + # Each stack entry is a tuple: (dfa, state, node). + # A node is a tuple: (type, value, context, children), + # where children is a list of nodes or None, and context may be None. + newnode = (start, None, None, []) + stackentry = (self._grammar_dfas[start], 0, newnode) + self.stack = [stackentry] + self.rootnode = None + self.used_names = set() # Aliased to self.rootnode.used_names in pop() + + def addtoken(self, type, value, context): + """Add a token; return True iff this is the end of the program.""" + cdef int ilabel, i, t, state, newstate + # Map from token to label + ilabel = self.classify(type, value, context) + # Loop until the token is shifted; may raise exceptions + while True: + dfa, state, node = self.stack[-1] + states, first = dfa + arcs = states[state] + # Look for a state with this label + for i, newstate in arcs: + t, v = self._grammar_labels[i] + if ilabel == i: + # Look it up in the list of labels + ## assert t < 256 + # Shift a token; we're done with it + self.shift(type, value, newstate, context) + # Pop while we are in an accept-only state + state = newstate + while states[state] == [(0, state)]: + self.pop() + if not self.stack: + # Done parsing! + return True + dfa, state, node = self.stack[-1] + states, first = dfa + # Done with this token + return False + elif t >= 256: + # See if it's a symbol and if we're in its first set + itsdfa = self._grammar_dfas[t] + itsstates, itsfirst = itsdfa + if ilabel in itsfirst: + # Push a symbol + self.push(t, itsdfa, newstate, context) + break # To continue the outer while loop + else: + if (0, state) in arcs: + # An accepting state, pop it and try something else + self.pop() + if not self.stack: + # Done parsing, but another token is input + raise ParseError("too much input", + type, value, context) + else: + # No success finding a transition + raise ParseError("bad input", type, value, context) + + cdef int classify(self, type, value, context): + """Turn a token into a label. (Internal)""" + if type == NAME: + # Keep a listing of all used names + self.used_names.add(value) + # Check for reserved words + ilabel = self._grammar_keywords.get(value) + if ilabel is not None: + return ilabel + ilabel = self._grammar_tokens.get(type) + if ilabel is None: + raise ParseError("bad token", type, value, context) + return ilabel + + cdef void shift(self, type, value, newstate, context): + """Shift a token. (Internal)""" + dfa, state, node = self.stack[-1] + newnode = (type, value, context, None) + newnode = self.convert(newnode) + if newnode is not None: + node[-1].append(newnode) + self.stack[-1] = (dfa, newstate, node) + + cdef void push(self, type, newdfa, newstate, context): + """Push a nonterminal. (Internal)""" + dfa, state, node = self.stack[-1] + newnode = (type, None, context, []) + self.stack[-1] = (dfa, newstate, node) + self.stack.append((newdfa, 0, newnode)) + + cdef void pop(self): + """Pop a nonterminal. (Internal)""" + popdfa, popstate, popnode = self.stack.pop() + newnode = self.convert(popnode) + if newnode is not None: + if self.stack: + dfa, state, node = self.stack[-1] + node[-1].append(newnode) + else: + self.rootnode = newnode + self.rootnode.used_names = self.used_names + + cdef convert(self, raw_node): + type, value, context, children = raw_node + if children or type in self._grammar_number2symbol: + # If there's exactly one child, return that child instead of + # creating a new node. + if len(children) == 1: + return children[0] + return Node(type, children, context=context) + else: + return Leaf(type, value, context=context) diff --git a/sphinx/pycode/pgen2/pgen.py b/sphinx/pycode/pgen2/pgen.py new file mode 100644 index 00000000..d6895eae --- /dev/null +++ b/sphinx/pycode/pgen2/pgen.py @@ -0,0 +1,384 @@ +# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +# Pgen imports +from sphinx.pycode.pgen2 import grammar, token, tokenize + +class PgenGrammar(grammar.Grammar): + pass + +class ParserGenerator(object): + + def __init__(self, filename, stream=None): + close_stream = None + if stream is None: + stream = open(filename) + close_stream = stream.close + self.filename = filename + self.stream = stream + self.generator = tokenize.generate_tokens(stream.readline) + self.gettoken() # Initialize lookahead + self.dfas, self.startsymbol = self.parse() + if close_stream is not None: + close_stream() + self.first = {} # map from symbol name to set of tokens + self.addfirstsets() + + def make_grammar(self): + c = PgenGrammar() + names = self.dfas.keys() + names.sort() + names.remove(self.startsymbol) + names.insert(0, self.startsymbol) + for name in names: + i = 256 + len(c.symbol2number) + c.symbol2number[name] = i + c.number2symbol[i] = name + for name in names: + dfa = self.dfas[name] + states = [] + for state in dfa: + arcs = [] + for label, next in state.arcs.iteritems(): + arcs.append((self.make_label(c, label), dfa.index(next))) + if state.isfinal: + arcs.append((0, dfa.index(state))) + states.append(arcs) + c.states.append(states) + c.dfas[c.symbol2number[name]] = (states, self.make_first(c, name)) + c.start = c.symbol2number[self.startsymbol] + return c + + def make_first(self, c, name): + rawfirst = self.first[name] + first = {} + for label in rawfirst: + ilabel = self.make_label(c, label) + ##assert ilabel not in first # XXX failed on <> ... != + first[ilabel] = 1 + return first + + def make_label(self, c, label): + # XXX Maybe this should be a method on a subclass of converter? + ilabel = len(c.labels) + if label[0].isalpha(): + # Either a symbol name or a named token + if label in c.symbol2number: + # A symbol name (a non-terminal) + if label in c.symbol2label: + return c.symbol2label[label] + else: + c.labels.append((c.symbol2number[label], None)) + c.symbol2label[label] = ilabel + return ilabel + else: + # A named token (NAME, NUMBER, STRING) + itoken = getattr(token, label, None) + assert isinstance(itoken, int), label + assert itoken in token.tok_name, label + if itoken in c.tokens: + return c.tokens[itoken] + else: + c.labels.append((itoken, None)) + c.tokens[itoken] = ilabel + return ilabel + else: + # Either a keyword or an operator + assert label[0] in ('"', "'"), label + value = eval(label) + if value[0].isalpha(): + # A keyword + if value in c.keywords: + return c.keywords[value] + else: + c.labels.append((token.NAME, value)) + c.keywords[value] = ilabel + return ilabel + else: + # An operator (any non-numeric token) + itoken = grammar.opmap[value] # Fails if unknown token + if itoken in c.tokens: + return c.tokens[itoken] + else: + c.labels.append((itoken, None)) + c.tokens[itoken] = ilabel + return ilabel + + def addfirstsets(self): + names = self.dfas.keys() + names.sort() + for name in names: + if name not in self.first: + self.calcfirst(name) + #print name, self.first[name].keys() + + def calcfirst(self, name): + dfa = self.dfas[name] + self.first[name] = None # dummy to detect left recursion + state = dfa[0] + totalset = {} + overlapcheck = {} + for label, next in state.arcs.iteritems(): + if label in self.dfas: + if label in self.first: + fset = self.first[label] + if fset is None: + raise ValueError("recursion for rule %r" % name) + else: + self.calcfirst(label) + fset = self.first[label] + totalset.update(fset) + overlapcheck[label] = fset + else: + totalset[label] = 1 + overlapcheck[label] = {label: 1} + inverse = {} + for label, itsfirst in overlapcheck.iteritems(): + for symbol in itsfirst: + if symbol in inverse: + raise ValueError("rule %s is ambiguous; %s is in the" + " first sets of %s as well as %s" % + (name, symbol, label, inverse[symbol])) + inverse[symbol] = label + self.first[name] = totalset + + def parse(self): + dfas = {} + startsymbol = None + # MSTART: (NEWLINE | RULE)* ENDMARKER + while self.type != token.ENDMARKER: + while self.type == token.NEWLINE: + self.gettoken() + # RULE: NAME ':' RHS NEWLINE + name = self.expect(token.NAME) + self.expect(token.OP, ":") + a, z = self.parse_rhs() + self.expect(token.NEWLINE) + #self.dump_nfa(name, a, z) + dfa = self.make_dfa(a, z) + #self.dump_dfa(name, dfa) + oldlen = len(dfa) + self.simplify_dfa(dfa) + newlen = len(dfa) + dfas[name] = dfa + #print name, oldlen, newlen + if startsymbol is None: + startsymbol = name + return dfas, startsymbol + + def make_dfa(self, start, finish): + # To turn an NFA into a DFA, we define the states of the DFA + # to correspond to *sets* of states of the NFA. Then do some + # state reduction. Let's represent sets as dicts with 1 for + # values. + assert isinstance(start, NFAState) + assert isinstance(finish, NFAState) + def closure(state): + base = {} + addclosure(state, base) + return base + def addclosure(state, base): + assert isinstance(state, NFAState) + if state in base: + return + base[state] = 1 + for label, next in state.arcs: + if label is None: + addclosure(next, base) + states = [DFAState(closure(start), finish)] + for state in states: # NB states grows while we're iterating + arcs = {} + for nfastate in state.nfaset: + for label, next in nfastate.arcs: + if label is not None: + addclosure(next, arcs.setdefault(label, {})) + for label, nfaset in arcs.iteritems(): + for st in states: + if st.nfaset == nfaset: + break + else: + st = DFAState(nfaset, finish) + states.append(st) + state.addarc(st, label) + return states # List of DFAState instances; first one is start + + def dump_nfa(self, name, start, finish): + print "Dump of NFA for", name + todo = [start] + for i, state in enumerate(todo): + print " State", i, state is finish and "(final)" or "" + for label, next in state.arcs: + if next in todo: + j = todo.index(next) + else: + j = len(todo) + todo.append(next) + if label is None: + print " -> %d" % j + else: + print " %s -> %d" % (label, j) + + def dump_dfa(self, name, dfa): + print "Dump of DFA for", name + for i, state in enumerate(dfa): + print " State", i, state.isfinal and "(final)" or "" + for label, next in state.arcs.iteritems(): + print " %s -> %d" % (label, dfa.index(next)) + + def simplify_dfa(self, dfa): + # This is not theoretically optimal, but works well enough. + # Algorithm: repeatedly look for two states that have the same + # set of arcs (same labels pointing to the same nodes) and + # unify them, until things stop changing. + + # dfa is a list of DFAState instances + changes = True + while changes: + changes = False + for i, state_i in enumerate(dfa): + for j in range(i+1, len(dfa)): + state_j = dfa[j] + if state_i == state_j: + #print " unify", i, j + del dfa[j] + for state in dfa: + state.unifystate(state_j, state_i) + changes = True + break + + def parse_rhs(self): + # RHS: ALT ('|' ALT)* + a, z = self.parse_alt() + if self.value != "|": + return a, z + else: + aa = NFAState() + zz = NFAState() + aa.addarc(a) + z.addarc(zz) + while self.value == "|": + self.gettoken() + a, z = self.parse_alt() + aa.addarc(a) + z.addarc(zz) + return aa, zz + + def parse_alt(self): + # ALT: ITEM+ + a, b = self.parse_item() + while (self.value in ("(", "[") or + self.type in (token.NAME, token.STRING)): + c, d = self.parse_item() + b.addarc(c) + b = d + return a, b + + def parse_item(self): + # ITEM: '[' RHS ']' | ATOM ['+' | '*'] + if self.value == "[": + self.gettoken() + a, z = self.parse_rhs() + self.expect(token.OP, "]") + a.addarc(z) + return a, z + else: + a, z = self.parse_atom() + value = self.value + if value not in ("+", "*"): + return a, z + self.gettoken() + z.addarc(a) + if value == "+": + return a, z + else: + return a, a + + def parse_atom(self): + # ATOM: '(' RHS ')' | NAME | STRING + if self.value == "(": + self.gettoken() + a, z = self.parse_rhs() + self.expect(token.OP, ")") + return a, z + elif self.type in (token.NAME, token.STRING): + a = NFAState() + z = NFAState() + a.addarc(z, self.value) + self.gettoken() + return a, z + else: + self.raise_error("expected (...) or NAME or STRING, got %s/%s", + self.type, self.value) + + def expect(self, type, value=None): + if self.type != type or (value is not None and self.value != value): + self.raise_error("expected %s/%s, got %s/%s", + type, value, self.type, self.value) + value = self.value + self.gettoken() + return value + + def gettoken(self): + tup = self.generator.next() + while tup[0] in (tokenize.COMMENT, tokenize.NL): + tup = self.generator.next() + self.type, self.value, self.begin, self.end, self.line = tup + #print token.tok_name[self.type], repr(self.value) + + def raise_error(self, msg, *args): + if args: + try: + msg = msg % args + except: + msg = " ".join([msg] + map(str, args)) + raise SyntaxError(msg, (self.filename, self.end[0], + self.end[1], self.line)) + +class NFAState(object): + + def __init__(self): + self.arcs = [] # list of (label, NFAState) pairs + + def addarc(self, next, label=None): + assert label is None or isinstance(label, str) + assert isinstance(next, NFAState) + self.arcs.append((label, next)) + +class DFAState(object): + + def __init__(self, nfaset, final): + assert isinstance(nfaset, dict) + assert isinstance(iter(nfaset).next(), NFAState) + assert isinstance(final, NFAState) + self.nfaset = nfaset + self.isfinal = final in nfaset + self.arcs = {} # map from label to DFAState + + def addarc(self, next, label): + assert isinstance(label, str) + assert label not in self.arcs + assert isinstance(next, DFAState) + self.arcs[label] = next + + def unifystate(self, old, new): + for label, next in self.arcs.iteritems(): + if next is old: + self.arcs[label] = new + + def __eq__(self, other): + # Equality test -- ignore the nfaset instance variable + assert isinstance(other, DFAState) + if self.isfinal != other.isfinal: + return False + # Can't just return self.arcs == other.arcs, because that + # would invoke this method recursively, with cycles... + if len(self.arcs) != len(other.arcs): + return False + for label, next in self.arcs.iteritems(): + if next is not other.arcs.get(label): + return False + return True + +def generate_grammar(filename="Grammar.txt"): + p = ParserGenerator(filename) + return p.make_grammar() diff --git a/sphinx/pycode/pgen2/token.py b/sphinx/pycode/pgen2/token.py new file mode 100755 index 00000000..61468b31 --- /dev/null +++ b/sphinx/pycode/pgen2/token.py @@ -0,0 +1,82 @@ +#! /usr/bin/env python + +"""Token constants (from "token.h").""" + +# Taken from Python (r53757) and modified to include some tokens +# originally monkeypatched in by pgen2.tokenize + +#--start constants-- +ENDMARKER = 0 +NAME = 1 +NUMBER = 2 +STRING = 3 +NEWLINE = 4 +INDENT = 5 +DEDENT = 6 +LPAR = 7 +RPAR = 8 +LSQB = 9 +RSQB = 10 +COLON = 11 +COMMA = 12 +SEMI = 13 +PLUS = 14 +MINUS = 15 +STAR = 16 +SLASH = 17 +VBAR = 18 +AMPER = 19 +LESS = 20 +GREATER = 21 +EQUAL = 22 +DOT = 23 +PERCENT = 24 +BACKQUOTE = 25 +LBRACE = 26 +RBRACE = 27 +EQEQUAL = 28 +NOTEQUAL = 29 +LESSEQUAL = 30 +GREATEREQUAL = 31 +TILDE = 32 +CIRCUMFLEX = 33 +LEFTSHIFT = 34 +RIGHTSHIFT = 35 +DOUBLESTAR = 36 +PLUSEQUAL = 37 +MINEQUAL = 38 +STAREQUAL = 39 +SLASHEQUAL = 40 +PERCENTEQUAL = 41 +AMPEREQUAL = 42 +VBAREQUAL = 43 +CIRCUMFLEXEQUAL = 44 +LEFTSHIFTEQUAL = 45 +RIGHTSHIFTEQUAL = 46 +DOUBLESTAREQUAL = 47 +DOUBLESLASH = 48 +DOUBLESLASHEQUAL = 49 +AT = 50 +OP = 51 +COMMENT = 52 +NL = 53 +RARROW = 54 +ERRORTOKEN = 55 +N_TOKENS = 56 +NT_OFFSET = 256 +#--end constants-- + +tok_name = {} +for _name, _value in globals().items(): + if type(_value) is type(0): + tok_name[_value] = _name + + +def ISTERMINAL(x): + return x < NT_OFFSET + +def ISNONTERMINAL(x): + return x >= NT_OFFSET + +def ISEOF(x): + return x == ENDMARKER diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py new file mode 100644 index 00000000..4489db89 --- /dev/null +++ b/sphinx/pycode/pgen2/tokenize.py @@ -0,0 +1,410 @@ +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation. +# All rights reserved. + +"""Tokenization help for Python programs. + +generate_tokens(readline) is a generator that breaks a stream of +text into Python tokens. It accepts a readline-like method which is called +repeatedly to get the next line of input (or "" for EOF). It generates +5-tuples with these members: + + the token type (see token.py) + the token (a string) + the starting (row, column) indices of the token (a 2-tuple of ints) + the ending (row, column) indices of the token (a 2-tuple of ints) + the original line (string) + +It is designed to match the working of the Python tokenizer exactly, except +that it produces COMMENT tokens for comments and gives type OP for all +operators + +Older entry points + tokenize_loop(readline, tokeneater) + tokenize(readline, tokeneater=printtoken) +are the same, except instead of generating tokens, tokeneater is a callback +function to which the 5 fields described above are passed as 5 arguments, +each time a new token is found.""" + +__author__ = 'Ka-Ping Yee <ping@lfw.org>' +__credits__ = \ + 'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro' + +import string, re +from sphinx.pycode.pgen2.token import * +from sphinx.pycode.pgen2 import token + +__all__ = [x for x in dir(token) if x[0] != '_'] + ["tokenize", + "generate_tokens", "untokenize"] +del token + +def group(*choices): return '(' + '|'.join(choices) + ')' +def any(*choices): return group(*choices) + '*' +def maybe(*choices): return group(*choices) + '?' + +Whitespace = r'[ \f\t]*' +Comment = r'#[^\r\n]*' +Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) +Name = r'[a-zA-Z_]\w*' + +Binnumber = r'0[bB][01]*' +Hexnumber = r'0[xX][\da-fA-F]*[lL]?' +Octnumber = r'0[oO]?[0-7]*[lL]?' +Decnumber = r'[1-9]\d*[lL]?' +Intnumber = group(Binnumber, Hexnumber, Octnumber, Decnumber) +Exponent = r'[eE][-+]?\d+' +Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent) +Expfloat = r'\d+' + Exponent +Floatnumber = group(Pointfloat, Expfloat) +Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]') +Number = group(Imagnumber, Floatnumber, Intnumber) + +# Tail end of ' string. +Single = r"[^'\\]*(?:\\.[^'\\]*)*'" +# Tail end of " string. +Double = r'[^"\\]*(?:\\.[^"\\]*)*"' +# Tail end of ''' string. +Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" +# Tail end of """ string. +Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' +Triple = group("[ubUB]?[rR]?'''", '[ubUB]?[rR]?"""') +# Single-line ' or " string. +String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'", + r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"') + +# Because of leftmost-then-longest match semantics, be sure to put the +# longest operators first (e.g., if = came before ==, == would get +# recognized as two instances of =). +Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=", + r"//=?", r"->", + r"[+\-*/%&|^=<>]=?", + r"~") + +Bracket = '[][(){}]' +Special = group(r'\r?\n', r'[:;.,`@]') +Funny = group(Operator, Bracket, Special) + +PlainToken = group(Number, Funny, String, Name) +Token = Ignore + PlainToken + +# First (or only) line of ' or " string. +ContStr = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" + + group("'", r'\\\r?\n'), + r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' + + group('"', r'\\\r?\n')) +PseudoExtras = group(r'\\\r?\n', Comment, Triple) +PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) + +tokenprog, pseudoprog, single3prog, double3prog = map( + re.compile, (Token, PseudoToken, Single3, Double3)) +endprogs = {"'": re.compile(Single), '"': re.compile(Double), + "'''": single3prog, '"""': double3prog, + "r'''": single3prog, 'r"""': double3prog, + "u'''": single3prog, 'u"""': double3prog, + "b'''": single3prog, 'b"""': double3prog, + "ur'''": single3prog, 'ur"""': double3prog, + "br'''": single3prog, 'br"""': double3prog, + "R'''": single3prog, 'R"""': double3prog, + "U'''": single3prog, 'U"""': double3prog, + "B'''": single3prog, 'B"""': double3prog, + "uR'''": single3prog, 'uR"""': double3prog, + "Ur'''": single3prog, 'Ur"""': double3prog, + "UR'''": single3prog, 'UR"""': double3prog, + "bR'''": single3prog, 'bR"""': double3prog, + "Br'''": single3prog, 'Br"""': double3prog, + "BR'''": single3prog, 'BR"""': double3prog, + 'r': None, 'R': None, + 'u': None, 'U': None, + 'b': None, 'B': None} + +triple_quoted = {} +for t in ("'''", '"""', + "r'''", 'r"""', "R'''", 'R"""', + "u'''", 'u"""', "U'''", 'U"""', + "b'''", 'b"""', "B'''", 'B"""', + "ur'''", 'ur"""', "Ur'''", 'Ur"""', + "uR'''", 'uR"""', "UR'''", 'UR"""', + "br'''", 'br"""', "Br'''", 'Br"""', + "bR'''", 'bR"""', "BR'''", 'BR"""',): + triple_quoted[t] = t +single_quoted = {} +for t in ("'", '"', + "r'", 'r"', "R'", 'R"', + "u'", 'u"', "U'", 'U"', + "b'", 'b"', "B'", 'B"', + "ur'", 'ur"', "Ur'", 'Ur"', + "uR'", 'uR"', "UR'", 'UR"', + "br'", 'br"', "Br'", 'Br"', + "bR'", 'bR"', "BR'", 'BR"', ): + single_quoted[t] = t + +tabsize = 8 + +class TokenError(Exception): pass + +class StopTokenizing(Exception): pass + +def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing + print "%d,%d-%d,%d:\t%s\t%s" % \ + (srow, scol, erow, ecol, tok_name[type], repr(token)) + +def tokenize(readline, tokeneater=printtoken): + """ + The tokenize() function accepts two parameters: one representing the + input stream, and one providing an output mechanism for tokenize(). + + The first parameter, readline, must be a callable object which provides + the same interface as the readline() method of built-in file objects. + Each call to the function should return one line of input as a string. + + The second parameter, tokeneater, must also be a callable object. It is + called once for each token, with five arguments, corresponding to the + tuples generated by generate_tokens(). + """ + try: + tokenize_loop(readline, tokeneater) + except StopTokenizing: + pass + +# backwards compatible interface +def tokenize_loop(readline, tokeneater): + for token_info in generate_tokens(readline): + tokeneater(*token_info) + +class Untokenizer: + + def __init__(self): + self.tokens = [] + self.prev_row = 1 + self.prev_col = 0 + + def add_whitespace(self, start): + row, col = start + assert row <= self.prev_row + col_offset = col - self.prev_col + if col_offset: + self.tokens.append(" " * col_offset) + + def untokenize(self, iterable): + for t in iterable: + if len(t) == 2: + self.compat(t, iterable) + break + tok_type, token, start, end, line = t + self.add_whitespace(start) + self.tokens.append(token) + self.prev_row, self.prev_col = end + if tok_type in (NEWLINE, NL): + self.prev_row += 1 + self.prev_col = 0 + return "".join(self.tokens) + + def compat(self, token, iterable): + startline = False + indents = [] + toks_append = self.tokens.append + toknum, tokval = token + if toknum in (NAME, NUMBER): + tokval += ' ' + if toknum in (NEWLINE, NL): + startline = True + for tok in iterable: + toknum, tokval = tok[:2] + + if toknum in (NAME, NUMBER): + tokval += ' ' + + if toknum == INDENT: + indents.append(tokval) + continue + elif toknum == DEDENT: + indents.pop() + continue + elif toknum in (NEWLINE, NL): + startline = True + elif startline and indents: + toks_append(indents[-1]) + startline = False + toks_append(tokval) + +def untokenize(iterable): + """Transform tokens back into Python source code. + + Each element returned by the iterable must be a token sequence + with at least two elements, a token number and token value. If + only two tokens are passed, the resulting output is poor. + + Round-trip invariant for full input: + Untokenized source will match input source exactly + + Round-trip invariant for limited intput: + # Output text will tokenize the back to the input + t1 = [tok[:2] for tok in generate_tokens(f.readline)] + newcode = untokenize(t1) + readline = iter(newcode.splitlines(1)).next + t2 = [tok[:2] for tokin generate_tokens(readline)] + assert t1 == t2 + """ + ut = Untokenizer() + return ut.untokenize(iterable) + +def generate_tokens(readline): + """ + The generate_tokens() generator requires one argment, readline, which + must be a callable object which provides the same interface as the + readline() method of built-in file objects. Each call to the function + should return one line of input as a string. Alternately, readline + can be a callable function terminating with StopIteration: + readline = open(myfile).next # Example of alternate readline + + The generator produces 5-tuples with these members: the token type; the + token string; a 2-tuple (srow, scol) of ints specifying the row and + column where the token begins in the source; a 2-tuple (erow, ecol) of + ints specifying the row and column where the token ends in the source; + and the line on which the token was found. The line passed is the + logical line; continuation lines are included. + """ + lnum = parenlev = continued = 0 + namechars, numchars = string.ascii_letters + '_', '0123456789' + contstr, needcont = '', 0 + contline = None + indents = [0] + + while 1: # loop over lines in stream + try: + line = readline() + except StopIteration: + line = '' + # if we are not at the end of the file make sure the + # line ends with a newline because the parser depends + # on that. + if line: + line = line.rstrip() + '\n' + lnum = lnum + 1 + pos, max = 0, len(line) + + if contstr: # continued string + if not line: + raise TokenError("EOF in multi-line string", strstart) + endmatch = endprog.match(line) + if endmatch: + pos = end = endmatch.end(0) + yield (STRING, contstr + line[:end], + strstart, (lnum, end), contline + line) + contstr, needcont = '', 0 + contline = None + elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': + yield (ERRORTOKEN, contstr + line, + strstart, (lnum, len(line)), contline) + contstr = '' + contline = None + continue + else: + contstr = contstr + line + contline = contline + line + continue + + elif parenlev == 0 and not continued: # new statement + if not line: break + column = 0 + while pos < max: # measure leading whitespace + if line[pos] == ' ': column = column + 1 + elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize + elif line[pos] == '\f': column = 0 + else: break + pos = pos + 1 + if pos == max: break + + if line[pos] in '#\r\n': # skip comments or blank lines + if line[pos] == '#': + comment_token = line[pos:].rstrip('\r\n') + nl_pos = pos + len(comment_token) + yield (COMMENT, comment_token, + (lnum, pos), (lnum, pos + len(comment_token)), line) + yield (NL, line[nl_pos:], + (lnum, nl_pos), (lnum, len(line)), line) + else: + yield ((NL, COMMENT)[line[pos] == '#'], line[pos:], + (lnum, pos), (lnum, len(line)), line) + continue + + if column > indents[-1]: # count indents or dedents + indents.append(column) + yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line) + while column < indents[-1]: + if column not in indents: + raise IndentationError( + "unindent does not match any outer indentation level", + ("<tokenize>", lnum, pos, line)) + indents = indents[:-1] + yield (DEDENT, '', (lnum, pos), (lnum, pos), line) + + else: # continued statement + if not line: + raise TokenError("EOF in multi-line statement", (lnum, 0)) + continued = 0 + + while pos < max: + pseudomatch = pseudoprog.match(line, pos) + if pseudomatch: # scan for tokens + start, end = pseudomatch.span(1) + spos, epos, pos = (lnum, start), (lnum, end), end + token, initial = line[start:end], line[start] + + if initial in numchars or \ + (initial == '.' and token != '.'): # ordinary number + yield (NUMBER, token, spos, epos, line) + elif initial in '\r\n': + newline = NEWLINE + if parenlev > 0: + newline = NL + yield (newline, token, spos, epos, line) + elif initial == '#': + assert not token.endswith("\n") + yield (COMMENT, token, spos, epos, line) + elif token in triple_quoted: + endprog = endprogs[token] + endmatch = endprog.match(line, pos) + if endmatch: # all on one line + pos = endmatch.end(0) + token = line[start:pos] + yield (STRING, token, spos, (lnum, pos), line) + else: + strstart = (lnum, start) # multiple lines + contstr = line[start:] + contline = line + break + elif initial in single_quoted or \ + token[:2] in single_quoted or \ + token[:3] in single_quoted: + if token[-1] == '\n': # continued string + strstart = (lnum, start) + endprog = (endprogs[initial] or endprogs[token[1]] or + endprogs[token[2]]) + contstr, needcont = line[start:], 1 + contline = line + break + else: # ordinary string + yield (STRING, token, spos, epos, line) + elif initial in namechars: # ordinary name + yield (NAME, token, spos, epos, line) + elif initial == '\\': # continued stmt + # This yield is new; needed for better idempotency: + yield (NL, token, spos, (lnum, pos), line) + continued = 1 + else: + if initial in '([{': parenlev = parenlev + 1 + elif initial in ')]}': parenlev = parenlev - 1 + yield (OP, token, spos, epos, line) + else: + yield (ERRORTOKEN, line[pos], + (lnum, pos), (lnum, pos+1), line) + pos = pos + 1 + + for indent in indents[1:]: # pop remaining indent levels + yield (DEDENT, '', (lnum, 0), (lnum, 0), '') + yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '') + +if __name__ == '__main__': # testing + import sys + if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline) + else: tokenize(sys.stdin.readline) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 173f26a1..cfdb97e7 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -15,8 +15,9 @@ from os import path TERM_ENCODING = getattr(sys.stdin, 'encoding', None) from sphinx.util import make_filename -from sphinx.util.console import purple, bold, red, turquoise, nocolor, color_terminal -from sphinx.util.texescape import tex_escape_map +from sphinx.util.console import purple, bold, red, turquoise, \ + nocolor, color_terminal +from sphinx.util import texescape PROMPT_PREFIX = '> ' @@ -29,9 +30,6 @@ QUICKSTART_CONF = '''\ # # This file is execfile()d with the current directory set to its containing dir. # -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). -# # Note that not all possible configuration values are present in this # autogenerated file. # @@ -40,13 +38,12 @@ QUICKSTART_CONF = '''\ import sys, os -# If your extensions (or modules documented by autodoc) are in another directory, +# If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) -# General configuration -# --------------------- +# -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -111,14 +108,23 @@ exclude_trees = [%(exclude_trees)s] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- -# Options for HTML output -# ----------------------- +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'default.css' +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". @@ -165,8 +171,8 @@ html_static_path = ['%(dot)sstatic'] # If true, the index is split into individual pages for each letter. #html_split_index = False -# If true, the reST sources are included in the HTML build as _sources/<name>. -#html_copy_source = True +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the @@ -180,8 +186,7 @@ html_static_path = ['%(dot)sstatic'] htmlhelp_basename = '%(project_fn)sdoc' -# Options for LaTeX output -# ------------------------ +# -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' @@ -190,7 +195,7 @@ htmlhelp_basename = '%(project_fn)sdoc' #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). +# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('%(master)s', '%(project_fn)s.tex', ur'%(project_doc_texescaped)s', ur'%(author_texescaped)s', 'manual'), @@ -217,11 +222,12 @@ latex_documents = [ INTERSPHINX_CONFIG = ''' # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/dev': None} +intersphinx_mapping = {'http://docs.python.org/': None} ''' MASTER_FILE = '''\ -.. %(project)s documentation master file, created by sphinx-quickstart on %(now)s. +.. %(project)s documentation master file, created by + sphinx-quickstart on %(now)s. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. @@ -257,50 +263,62 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d %(rbuilddir)s/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s -.PHONY: help clean html web pickle htmlhelp latex changes linkcheck +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes \ +linkcheck doctest help: \t@echo "Please use \\`make <target>' where <target> is one of" \t@echo " html to make standalone HTML files" +\t@echo " dirhtml to make HTML files named index.html in directories" \t@echo " pickle to make pickle files" \t@echo " json to make JSON files" \t@echo " htmlhelp to make HTML files and a HTML help project" +\t@echo " qthelp to make HTML files and a qthelp project" \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" -\t@echo " changes to make an overview over all changed/added/deprecated items" +\t@echo " changes to make an overview of all changed/added/deprecated items" \t@echo " linkcheck to check all external links for integrity" +\t@echo " doctest to run all doctests embedded in the documentation \ +(if enabled)" clean: \t-rm -rf %(rbuilddir)s/* html: -\tmkdir -p %(rbuilddir)s/html %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) %(rbuilddir)s/html \t@echo \t@echo "Build finished. The HTML pages are in %(rbuilddir)s/html." +dirhtml: +\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) %(rbuilddir)s/dirhtml +\t@echo +\t@echo "Build finished. The HTML pages are in %(rbuilddir)s/dirhtml." + pickle: -\tmkdir -p %(rbuilddir)s/pickle %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) %(rbuilddir)s/pickle \t@echo \t@echo "Build finished; now you can process the pickle files." -web: pickle - json: -\tmkdir -p %(rbuilddir)s/json %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) %(rbuilddir)s/json \t@echo \t@echo "Build finished; now you can process the JSON files." htmlhelp: -\tmkdir -p %(rbuilddir)s/htmlhelp %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) %(rbuilddir)s/htmlhelp \t@echo \t@echo "Build finished; now you can run HTML Help Workshop with the" \\ \t ".hhp project file in %(rbuilddir)s/htmlhelp." +qthelp: +\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) %(rbuilddir)s/qthelp +\t@echo +\t@echo "Build finished; now you can run "qcollectiongenerator" with the" \\ +\t ".qhcp project file in %(rbuilddir)s/qthelp, like this:" +\t@echo "# qcollectiongenerator %(rbuilddir)s/qthelp/%(project)s.qhcp" +\t@echo "To view the help file:" +\t@echo "# assistant -collectionFile %(rbuilddir)s/qthelp/%(project)s.qhc" + latex: -\tmkdir -p %(rbuilddir)s/latex %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) %(rbuilddir)s/latex \t@echo \t@echo "Build finished; the LaTeX files are in %(rbuilddir)s/latex." @@ -308,17 +326,135 @@ latex: \t "run these through (pdf)latex." changes: -\tmkdir -p %(rbuilddir)s/changes %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) %(rbuilddir)s/changes \t@echo \t@echo "The overview file is in %(rbuilddir)s/changes." linkcheck: -\tmkdir -p %(rbuilddir)s/linkcheck %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) %(rbuilddir)s/linkcheck \t@echo \t@echo "Link check complete; look for any errors in the above output " \\ \t "or in %(rbuilddir)s/linkcheck/output.txt." + +doctest: +\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) %(rbuilddir)s/doctest +\t@echo "Testing of doctests in the sources finished, look at the " \\ +\t "results in %(rbuilddir)s/doctest/output.txt." +''' + +BATCHFILE = '''\ +@ECHO OFF + +REM Command file for Sphinx documentation + +set SPHINXBUILD=sphinx-build +set ALLSPHINXOPTS=-d %(rbuilddir)s/doctrees %%SPHINXOPTS%% %(rsrcdir)s +if NOT "%%PAPER%%" == "" ( +\tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%% +) + +if "%%1" == "" goto help + +if "%%1" == "help" ( +\t:help +\techo.Please use `make ^<target^>` where ^<target^> is one of +\techo. html to make standalone HTML files +\techo. dirhtml to make HTML files named index.html in directories +\techo. pickle to make pickle files +\techo. json to make JSON files +\techo. htmlhelp to make HTML files and a HTML help project +\techo. qthelp to make HTML files and a qthelp project +\techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter +\techo. changes to make an overview over all changed/added/deprecated items +\techo. linkcheck to check all external links for integrity +\techo. doctest to run all doctests embedded in the documentation if enabled +\tgoto end +) + +if "%%1" == "clean" ( +\tfor /d %%%%i in (%(rbuilddir)s\*) do rmdir /q /s %%%%i +\tdel /q /s %(rbuilddir)s\* +\tgoto end +) + +if "%%1" == "html" ( +\t%%SPHINXBUILD%% -b html %%ALLSPHINXOPTS%% %(rbuilddir)s/html +\techo. +\techo.Build finished. The HTML pages are in %(rbuilddir)s/html. +\tgoto end +) + +if "%%1" == "dirhtml" ( +\t%%SPHINXBUILD%% -b dirhtml %%ALLSPHINXOPTS%% %(rbuilddir)s/dirhtml +\techo. +\techo.Build finished. The HTML pages are in %(rbuilddir)s/dirhtml. +\tgoto end +) + +if "%%1" == "pickle" ( +\t%%SPHINXBUILD%% -b pickle %%ALLSPHINXOPTS%% %(rbuilddir)s/pickle +\techo. +\techo.Build finished; now you can process the pickle files. +\tgoto end +) + +if "%%1" == "json" ( +\t%%SPHINXBUILD%% -b json %%ALLSPHINXOPTS%% %(rbuilddir)s/json +\techo. +\techo.Build finished; now you can process the JSON files. +\tgoto end +) + +if "%%1" == "htmlhelp" ( +\t%%SPHINXBUILD%% -b htmlhelp %%ALLSPHINXOPTS%% %(rbuilddir)s/htmlhelp +\techo. +\techo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %(rbuilddir)s/htmlhelp. +\tgoto end +) + +if "%%1" == "qthelp" ( +\t%%SPHINXBUILD%% -b qthelp %%ALLSPHINXOPTS%% %(rbuilddir)s/qthelp +\techo. +\techo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %(rbuilddir)s/qthelp, like this: +\techo.^> qcollectiongenerator %(rbuilddir)s\\qthelp\\%(project)s.qhcp +\techo.To view the help file: +\techo.^> assistant -collectionFile %(rbuilddir)s\\qthelp\\%(project)s.ghc +\tgoto end +) + +if "%%1" == "latex" ( +\t%%SPHINXBUILD%% -b latex %%ALLSPHINXOPTS%% %(rbuilddir)s/latex +\techo. +\techo.Build finished; the LaTeX files are in %(rbuilddir)s/latex. +\tgoto end +) + +if "%%1" == "changes" ( +\t%%SPHINXBUILD%% -b changes %%ALLSPHINXOPTS%% %(rbuilddir)s/changes +\techo. +\techo.The overview file is in %(rbuilddir)s/changes. +\tgoto end +) + +if "%%1" == "linkcheck" ( +\t%%SPHINXBUILD%% -b linkcheck %%ALLSPHINXOPTS%% %(rbuilddir)s/linkcheck +\techo. +\techo.Link check complete; look for any errors in the above output ^ +or in %(rbuilddir)s/linkcheck/output.txt. +\tgoto end +) + +if "%%1" == "doctest" ( +\t%%SPHINXBUILD%% -b doctest %%ALLSPHINXOPTS%% %(rbuilddir)s/doctest +\techo. +\techo.Testing of doctests in the sources finished, look at the ^ +results in %(rbuilddir)s/doctest/output.txt. +\tgoto end +) + +:end ''' @@ -367,8 +503,9 @@ def do_prompt(d, key, text, default=None, validator=nonempty): if TERM_ENCODING: x = x.decode(TERM_ENCODING) else: - print turquoise('* Note: non-ASCII characters entered and terminal ' - 'encoding unknown -- assuming UTF-8 or Latin-1.') + print turquoise('* Note: non-ASCII characters entered ' + 'and terminal encoding unknown -- assuming ' + 'UTF-8 or Latin-1.') try: x = x.decode('utf-8') except UnicodeDecodeError: @@ -382,6 +519,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): def inner_main(args): d = {} + texescape.init() if not sys.stdout.isatty() or not color_terminal(): nocolor() @@ -402,23 +540,23 @@ Enter the root path for documentation.''' 'selected root path.') print 'sphinx-quickstart will not overwrite existing Sphinx projects.' print - do_prompt(d, 'path', 'Please enter a new root path (or just Enter to exit)', - '', is_path) + do_prompt(d, 'path', 'Please enter a new root path (or just Enter ' + 'to exit)', '', is_path) if not d['path']: sys.exit(1) print ''' You have two options for placing the build directory for Sphinx output. -Either, you use a directory ".build" within the root path, or you separate +Either, you use a directory "_build" within the root path, or you separate "source" and "build" directories within the root path.''' do_prompt(d, 'sep', 'Separate source and build directories (y/N)', 'n', boolean) + print ''' -Inside the root directory, two more directories will be created; ".templates" -for custom HTML templates and ".static" for custom stylesheets and other -static files. Since the leading dot may be inconvenient for Windows users, -you can enter another prefix (such as "_") to replace the dot.''' - do_prompt(d, 'dot', 'Name prefix for templates and static dir', '.', ok) +Inside the root directory, two more directories will be created; "_templates" +for custom HTML templates and "_static" for custom stylesheets and other static +files. You can enter another prefix (such as ".") to replace the underscore.''' + do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok) print ''' The project name will occur in several places in the built documentation.''' @@ -449,26 +587,43 @@ Please indicate if you want to use one of the following Sphinx extensions:''' 'from modules (y/N)', 'n', boolean) do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets ' 'in doctest blocks (y/N)', 'n', boolean) - do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx documentation ' - 'of different projects (y/N)', 'n', boolean) + do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx ' + 'documentation of different projects (y/N)', 'n', boolean) + do_prompt(d, 'ext_todo', 'todo: write "todo" entries ' + 'that can be shown or hidden on build (y/N)', 'n', boolean) + do_prompt(d, 'ext_coverage', 'coverage: checks for documentation ' + 'coverage (y/N)', 'n', boolean) + do_prompt(d, 'ext_pngmath', 'pngmath: include math, rendered ' + 'as PNG images (y/N)', 'n', boolean) + do_prompt(d, 'ext_jsmath', 'jsmath: include math, rendered in the ' + 'browser by JSMath (y/N)', 'n', boolean) + if d['ext_pngmath'] and d['ext_jsmath']: + print '''Note: pngmath and jsmath cannot be enabled at the same time. +pngmath has been deselected.''' + do_prompt(d, 'ext_ifconfig', 'ifconfig: conditional inclusion of ' + 'content based on config values (y/N)', 'n', boolean) print ''' -If you are under Unix, a Makefile can be generated for you so that you +A Makefile and a Windows command file can be generated for you so that you only have to run e.g. `make html' instead of invoking sphinx-build directly.''' - do_prompt(d, 'makefile', 'Create Makefile? (Y/n)', - os.name == 'posix' and 'y' or 'n', boolean) + do_prompt(d, 'makefile', 'Create Makefile? (Y/n)', 'y', boolean) + do_prompt(d, 'batchfile', 'Create Windows command file? (Y/n)', + 'y', boolean) d['project_fn'] = make_filename(d['project']) d['now'] = time.asctime() d['underline'] = len(d['project']) * '=' d['extensions'] = ', '.join( - repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest', 'intersphinx') + repr('sphinx.ext.' + name) + for name in ('autodoc', 'doctest', 'intersphinx', 'todo', 'coverage', + 'pngmath', 'jsmath', 'ifconfig') if d['ext_' + name].upper() in ('Y', 'YES')) d['copyright'] = time.strftime('%Y') + ', ' + d['author'] - d['author_texescaped'] = unicode(d['author']).translate(tex_escape_map) + d['author_texescaped'] = unicode(d['author']).\ + translate(texescape.tex_escape_map) d['project_doc'] = d['project'] + ' Documentation' - d['project_doc_texescaped'] = \ - unicode(d['project'] + ' Documentation').translate(tex_escape_map) + d['project_doc_texescaped'] = unicode(d['project'] + ' Documentation').\ + translate(texescape.tex_escape_map) if not path.isdir(d['path']): mkdir_p(d['path']) @@ -508,11 +663,19 @@ directly.''' f.write((MAKEFILE % d).encode('utf-8')) f.close() + create_batch = d['batchfile'].upper() in ('Y', 'YES') + if create_batch: + d['rsrcdir'] = separate and 'source' or '.' + d['rbuilddir'] = separate and 'build' or d['dot'] + 'build' + f = open(path.join(d['path'], 'make.bat'), 'w') + f.write((BATCHFILE % d).encode('utf-8')) + f.close() + print print bold('Finished: An initial directory structure has been created.') print ''' You should now populate your master file %s and create other documentation -source files. ''' % masterfile + (create_makefile and '''\ +source files. ''' % masterfile + ((create_makefile or create_batch) and '''\ Use the Makefile to build the docs, like so: make builder ''' or '''\ diff --git a/sphinx/roles.py b/sphinx/roles.py index a01f5860..550deb3e 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -24,7 +24,7 @@ generic_docroles = { 'guilabel' : nodes.strong, 'kbd' : nodes.literal, 'mailheader' : addnodes.literal_emphasis, - 'makevar' : nodes.Text, + 'makevar' : nodes.strong, 'manpage' : addnodes.literal_emphasis, 'mimetype' : addnodes.literal_emphasis, 'newsgroup' : addnodes.literal_emphasis, @@ -37,7 +37,8 @@ for rolename, nodeclass in generic_docroles.iteritems(): roles.register_local_role(rolename, role) -def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]): +def indexmarkup_role(typ, rawtext, etext, lineno, inliner, + options={}, content=[]): env = inliner.document.settings.env if not typ: typ = env.config.default_role @@ -57,13 +58,14 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[ options, content)[0] return [indexnode, targetnode] + xref_nodes, [] elif typ == 'pep': - indexnode['entries'] = [('single', - _('Python Enhancement Proposals!PEP %s') % text, - targetid, 'PEP %s' % text)] + indexnode['entries'] = [ + ('single', _('Python Enhancement Proposals!PEP %s') % text, + targetid, 'PEP %s' % text)] try: pepnum = int(text) except ValueError: - msg = inliner.reporter.error('invalid PEP number %s' % text, line=lineno) + msg = inliner.reporter.error('invalid PEP number %s' % text, + line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum @@ -77,7 +79,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[ try: rfcnum = int(text) except ValueError: - msg = inliner.reporter.error('invalid RFC number %s' % text, line=lineno) + msg = inliner.reporter.error('invalid RFC number %s' % text, + line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum @@ -97,6 +100,7 @@ innernodetypes = { 'term': nodes.emphasis, 'token': nodes.strong, 'envvar': nodes.strong, + 'download': nodes.strong, 'option': addnodes.literal_emphasis, } @@ -123,11 +127,14 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [innernodetypes.get(typ, nodes.literal)( rawtext, text, classes=['xref'])], [] # we want a cross-reference, create the reference node - pnode = addnodes.pending_xref(rawtext, reftype=typ, refcaption=False, - modname=env.currmodule, classname=env.currclass) + nodeclass = (typ == 'download') and addnodes.download_reference or \ + addnodes.pending_xref + pnode = nodeclass(rawtext, reftype=typ, refcaption=False, + modname=env.currmodule, classname=env.currclass) # we may need the line number for warnings pnode.line = lineno - # the link title may differ from the target, but by default they are the same + # the link title may differ from the target, but by default + # they are the same title = target = text titleistarget = True # look if explicit title and target are given with `foo <bar>` syntax @@ -144,7 +151,8 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): target = text[brace+1:] title = text[:brace] # special target for Python object cross-references - if typ in ('data', 'exc', 'func', 'class', 'const', 'attr', 'meth', 'mod', 'obj'): + if typ in ('data', 'exc', 'func', 'class', 'const', 'attr', + 'meth', 'mod', 'obj'): # fix-up parentheses in link title if titleistarget: title = title.lstrip('.') # only has a meaning for the target @@ -169,7 +177,8 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): elif typ == 'option': program = env.currprogram if titleistarget: - if ' ' in title and not (title.startswith('/') or title.startswith('-')): + if ' ' in title and not (title.startswith('/') or + title.startswith('-')): program, target = re.split(' (?=-|--|/)', title, 1) program = ws_re.sub('-', program) target = target.strip() @@ -188,18 +197,21 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # remove all whitespace to avoid referencing problems target = ws_re.sub('', target) pnode['reftarget'] = target - pnode += innernodetypes.get(typ, nodes.literal)(rawtext, title, classes=['xref']) + pnode += innernodetypes.get(typ, nodes.literal)(rawtext, title, + classes=['xref']) return [pnode], [] def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [nodes.emphasis( - rawtext, utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], [] + rawtext, + utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], [] _litvar_re = re.compile('{([^}]+)}') -def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): +def emph_literal_role(typ, rawtext, text, lineno, inliner, + options={}, content=[]): text = utils.unescape(text) pos = 0 retnode = nodes.literal(role=typ.lower()) @@ -214,6 +226,18 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[ return [retnode], [] +_abbr_re = re.compile('\((.*)\)$') + +def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + text = utils.unescape(text) + m = _abbr_re.search(text) + if m is None: + return [addnodes.abbreviation(text, text)], [] + abbr = text[:m.start()].strip() + expl = m.group(1) + return [addnodes.abbreviation(abbr, abbr, explanation=expl)], [] + + specific_docroles = { 'data': xfileref_role, 'exc': xfileref_role, @@ -236,10 +260,13 @@ specific_docroles = { 'token': xfileref_role, 'term': xfileref_role, 'option': xfileref_role, + 'doc': xfileref_role, + 'download': xfileref_role, 'menuselection': menusel_role, 'file': emph_literal_role, 'samp': emph_literal_role, + 'abbr': abbr_role, } for rolename, func in specific_docroles.iteritems(): diff --git a/sphinx/search.py b/sphinx/search.py index f39d3674..fe20c24a 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -134,13 +134,16 @@ class IndexBuilder(object): def get_modules(self, fn2index): rv = {} for name, (doc, _, _, _) in self.env.modules.iteritems(): - rv[name] = fn2index[doc] + if doc in fn2index: + rv[name] = fn2index[doc] return rv def get_descrefs(self, fn2index): rv = {} dt = self._desctypes for fullname, (doc, desctype) in self.env.descrefs.iteritems(): + if doc not in fn2index: + continue prefix, name = rpartition(fullname, '.') pdict = rv.setdefault(prefix, {}) try: @@ -156,9 +159,10 @@ class IndexBuilder(object): for k, v in self._mapping.iteritems(): if len(v) == 1: fn, = v - rv[k] = fn2index[fn] + if fn in fn2index: + rv[k] = fn2index[fn] else: - rv[k] = [fn2index[fn] for fn in v] + rv[k] = [fn2index[fn] for fn in v if fn in fn2index] return rv def freeze(self): diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index c874bdd4..ac395f39 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -84,6 +84,7 @@ class BuildDoc(Command): from docutils.utils import SystemMessage if isinstance(err, SystemMessage): sys.stderr, darkred('reST markup error:') - print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace') + print >>sys.stderr, err.args[0].encode('ascii', + 'backslashreplace') else: raise diff --git a/sphinx/static/rightsidebar.css b/sphinx/static/rightsidebar.css deleted file mode 100644 index bc604a89..00000000 --- a/sphinx/static/rightsidebar.css +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Sphinx Doc Design -- Right Side Bar Overrides - */ - - -div.sphinxsidebar { - float: right; -} - -div.bodywrapper { - margin: 0 230px 0 0; -} - -div.inlinecomments { - right: 250px; -} diff --git a/sphinx/static/stickysidebar.css b/sphinx/static/stickysidebar.css deleted file mode 100644 index dfc99c77..00000000 --- a/sphinx/static/stickysidebar.css +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Sphinx Doc Design -- Sticky sidebar Overrides - */ - -div.sphinxsidebar { - top: 30px; - left: 0px; - position: fixed; - margin: 0; - float: none; -} - -div.related { - position: fixed; -} - -div.documentwrapper { - margin-top: 30px; -} diff --git a/sphinx/templates/page.html b/sphinx/templates/page.html deleted file mode 100644 index 4de23b13..00000000 --- a/sphinx/templates/page.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "layout.html" %} -{% set page_links = [ - (pathto('@rss/' + sourcename), 'application/rss+xml', 'Page Comments'), -] %} -{% block body %} - {% if oldurl %} - <div class="docwarning"> - {% trans %}<strong>Note:</strong> You requested an out-of-date URL from this server. We've tried to redirect you to the new location of this page, but it may not be the right one.{% endtrans %} - </div> - {% endif %} - {{ body }} -{% endblock %} diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index c799d688..a2bc3dbf 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -17,11 +17,15 @@ \RequirePackage{makeidx} \RequirePackage{framed} \RequirePackage{color} +% For highlighted code. \RequirePackage{fancyvrb} +% For table captions. \RequirePackage{threeparttable} % Handle footnotes in tables. \RequirePackage{footnote} \makesavenoteenv{tabulary} +% For floating figures in the text. +\RequirePackage{wrapfig} % Redefine these colors to your liking in the preamble. \definecolor{TitleColor}{rgb}{0.126,0.263,0.361} @@ -454,6 +458,20 @@ \fi }{\end{fulllineitems}} +% class method ---------------------------------------------------------- +% \begin{classmethoddesc}[classname]{methodname}{args} +\newcommand{\classmethodline}[3][\@undefined]{ + \py@sigline{class \bfcode{#2}}{#3}} +\newenvironment{classmethoddesc}[3][\@undefined]{ + \begin{fulllineitems} + \ifx\@undefined#1\relax + \classmethodline{#2}{#3} + \else + \def\py@thisclass{#1} + \classmethodline{#2}{#3} + \fi +}{\end{fulllineitems}} + % object data attribute -------------------------------------------------- % \begin{memberdesc}[classname]{membername} \newcommand{\memberline}[2][\py@classbadkey]{% diff --git a/sphinx/templates/changes/frameset.html b/sphinx/themes/basic/changes/frameset.html index 9d9af9eb..9d9af9eb 100644 --- a/sphinx/templates/changes/frameset.html +++ b/sphinx/themes/basic/changes/frameset.html diff --git a/sphinx/templates/changes/rstsource.html b/sphinx/themes/basic/changes/rstsource.html index abd12c1d..abd12c1d 100644 --- a/sphinx/templates/changes/rstsource.html +++ b/sphinx/themes/basic/changes/rstsource.html diff --git a/sphinx/templates/changes/versionchanges.html b/sphinx/themes/basic/changes/versionchanges.html index 14d5efd3..09651bf1 100644 --- a/sphinx/templates/changes/versionchanges.html +++ b/sphinx/themes/basic/changes/versionchanges.html @@ -1,4 +1,4 @@ -{% macro entries changes %} +{% macro entries(changes) %} <ul>{% for entry, docname, lineno in changes %} <li><a href="rst/{{ docname }}.html#L{{ lineno-10 }}" target="src">{{ entry }}</a></li> {% endfor %}</ul> diff --git a/sphinx/templates/defindex.html b/sphinx/themes/basic/defindex.html index 40f4f4c9..40f4f4c9 100644 --- a/sphinx/templates/defindex.html +++ b/sphinx/themes/basic/defindex.html diff --git a/sphinx/templates/genindex-single.html b/sphinx/themes/basic/genindex-single.html index 9aaaeb0c..9aaaeb0c 100644 --- a/sphinx/templates/genindex-single.html +++ b/sphinx/themes/basic/genindex-single.html diff --git a/sphinx/templates/genindex-split.html b/sphinx/themes/basic/genindex-split.html index ab099e5b..ab099e5b 100644 --- a/sphinx/templates/genindex-split.html +++ b/sphinx/themes/basic/genindex-split.html diff --git a/sphinx/templates/genindex.html b/sphinx/themes/basic/genindex.html index a19aa80f..a19aa80f 100644 --- a/sphinx/templates/genindex.html +++ b/sphinx/themes/basic/genindex.html diff --git a/sphinx/templates/layout.html b/sphinx/themes/basic/layout.html index 9f566022..dd2e2d51 100644 --- a/sphinx/templates/layout.html +++ b/sphinx/themes/basic/layout.html @@ -4,28 +4,30 @@ {%- endblock %} {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} -{%- macro relbar %} + +{%- macro relbar() %} <div class="related"> <h3>{{ _('Navigation') }}</h3> <ul> {%- for rellink in rellinks %} <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}> <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}" - accesskey="{{ rellink[2] }}">{{ rellink[3] }}</a> + {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a> {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li> {%- endfor %} {%- block rootrellink %} <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li> {%- endblock %} {%- for parent in parents %} - <li><a href="{{ parent.link|e }}" accesskey="U">{{ parent.title }}</a>{{ reldelim1 }}</li> + <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li> {%- endfor %} {%- block relbaritems %} {% endblock %} </ul> </div> {%- endmacro %} -{%- macro sidebar %} - {%- if builder != 'htmlhelp' %} + +{%- macro sidebar() %} + {%- if not embedded %}{% if not theme_nosidebar|tobool %} <div class="sphinxsidebar"> <div class="sphinxsidebarwrapper"> {%- block sidebarlogo %} @@ -44,65 +46,62 @@ {%- block sidebarrel %} {%- if prev %} <h4>{{ _('Previous topic') }}</h4> - <p class="topless"><a href="{{ prev.link|e }}" title="{{ _('previous chapter') }}">{{ prev.title }}</a></p> + <p class="topless"><a href="{{ prev.link|e }}" + title="{{ _('previous chapter') }}">{{ prev.title }}</a></p> {%- endif %} {%- if next %} <h4>{{ _('Next topic') }}</h4> - <p class="topless"><a href="{{ next.link|e }}" title="{{ _('next chapter') }}">{{ next.title }}</a></p> + <p class="topless"><a href="{{ next.link|e }}" + title="{{ _('next chapter') }}">{{ next.title }}</a></p> {%- endif %} {%- endblock %} - {%- if sourcename %} + {%- block sidebarsourcelink %} + {%- if show_source and has_source and sourcename %} <h3>{{ _('This Page') }}</h3> <ul class="this-page-menu"> - {%- if builder == 'web' %} - <li><a href="#comments">Comments ({{ comments|length }} so far)</a></li> - <li><a href="{{ pathto('@edit/' + sourcename)|e }}">{{ _('Suggest Change') }}</a></li> - <li><a href="{{ pathto('@source/' + sourcename)|e }}">{{ _('Show Source') }}</a></li> - {%- elif builder == 'html' %} - <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">{{ _('Show Source') }}</a></li> - {%- endif %} + <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}" + rel="nofollow">{{ _('Show Source') }}</a></li> </ul> {%- endif %} + {%- endblock %} {%- if customsidebar %} - {{ rendertemplate(customsidebar) }} + {% include customsidebar %} {%- endif %} {%- block sidebarsearch %} {%- if pagename != "search" %} - <h3>{% if builder == 'web' %}{{ _('Keyword search')}}{% else %}{{ _('Quick search') }}{% endif %}</h3> - <form class="search" action="{{ pathto('search') }}" method="get"> - <input type="text" name="q" size="18" /> <input type="submit" value="{{ _('Go') }}" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - {%- if builder == 'web' %} - <p style="font-size: 90%">{{ _('Enter a module, class or function name.') }}</p> - {%- endif %} + <div id="searchbox" style="display: none"> + <h3>{{ _('Quick search') }}</h3> + <form class="search" action="{{ pathto('search') }}" method="get"> + <input type="text" name="q" size="18" /> + <input type="submit" value="{{ _('Go') }}" /> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + </form> + <p class="searchtip" style="font-size: 90%"> + {{ _('Enter search terms or a module, class or function name.') }} + </p> + </div> + <script type="text/javascript">$('#searchbox').show(0);</script> {%- endif %} {%- endblock %} </div> </div> - {%- endif %} -{%- endmacro -%} + {%- endif %}{% endif %} +{%- endmacro %} <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> {{ metatags }} - {%- if builder != 'htmlhelp' %} - {%- set titlesuffix = " — " + docstitle|e %} + {%- if not embedded %} + {%- set titlesuffix = " — "|safe + docstitle|e %} + {%- else %} + {%- set titlesuffix = "" %} {%- endif %} <title>{{ title|striptags }}{{ titlesuffix }}</title> - {%- if builder == 'web' %} - <link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{% - if in_admin_panel %}&admin=yes{% endif %}" type="text/css" /> - {%- for link, type, title in page_links %} - <link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}" /> - {%- endfor %} - {%- else %} <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /> <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" /> - {%- endif %} - {%- if builder != 'htmlhelp' %} + {%- if not embedded %} <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '{{ pathto("", 1) }}', @@ -151,23 +150,24 @@ {%- block extrahead %} {% endblock %} </head> <body> +{%- block header %}{% endblock %} {%- block relbar1 %}{{ relbar() }}{% endblock %} {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} -{%- block document %} <div class="document"> +{%- block document %} <div class="documentwrapper"> - {%- if builder != 'htmlhelp' %} + {%- if not embedded %}{% if not theme_nosidebar|tobool %} <div class="bodywrapper"> - {%- endif %} + {%- endif %}{% endif %} <div class="body"> {% block body %} {% endblock %} </div> - {%- if builder != 'htmlhelp' %} + {%- if not embedded %}{% if not theme_nosidebar|tobool %} </div> - {%- endif %} + {%- endif %}{% endif %} </div> {%- endblock %} diff --git a/sphinx/templates/modindex.html b/sphinx/themes/basic/modindex.html index d6b505da..0392edc8 100644 --- a/sphinx/templates/modindex.html +++ b/sphinx/themes/basic/modindex.html @@ -2,7 +2,7 @@ {% set title = _('Global Module Index') %} {% block extrahead %} {{ super() }} -{% if builder != 'htmlhelp' and collapse_modindex %} +{% if not embedded and collapse_modindex %} <script type="text/javascript"> DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX = true; </script> @@ -11,26 +11,6 @@ {% block body %} <h1 id="global-module-index">{{ _('Global Module Index') }}</h1> -{% if builder == 'web' and freqentries %} - <p>{{ _('Most popular modules:') }}</p> - <div class="modulecloud"> - {%- for module in freqentries %} - <a href="../q/{{ module.name|e }}/" style="font-size: {{ module.size }}%">{{ module.name|e }}</a> - {%- endfor %} - </div> -{% endif %} -{% if builder == 'web' %} - <form class="pfform" action="" method="get"> - {{ _('Show modules only available on these platforms') }}:<br> - {% for pl in platforms -%} - <input type="checkbox" name="pf" value="{{ pl }}" id="pl-{{ pl }}" - {%- if pl in showpf %} checked="checked"{% endif %}> - <label for="pl-{{ pl }}">{{ pl }}</label> - {% endfor %} - <input type="hidden" name="newpf" value="true"> - <input type="submit" value="Apply"> - </form> -{% endif %} {%- for letter in letters %} <a href="#cap-{{ letter }}"><strong>{{ letter }}</strong></a> {% if not loop.last %}| {% endif %} @@ -38,7 +18,7 @@ <hr/> <table width="100%" class="indextable" cellspacing="0" cellpadding="2"> - {%- for modname, collapse, cgroup, indent, fname, synops, pform, dep in modindexentries %} + {%- for modname, collapse, cgroup, indent, fname, synops, pform, dep, stripped in modindexentries %} {%- if not modname -%} <tr class="pcap"><td></td><td> </td><td></td></tr> <tr class="cap"><td></td><td><a name="cap-{{ fname }}"><strong>{{ fname }}</strong></a></td><td></td></tr> @@ -50,9 +30,9 @@ {%- endif %}</td> <td>{% if indent %} {% endif %} {% if fname %}<a href="{{ fname }}">{% endif -%} - <tt class="xref">{{ modname|e }}</tt> + <tt class="xref">{{ stripped|e }}{{ modname|e }}</tt> {%- if fname %}</a>{% endif %} - {%- if pform[0] %} <em>({{ pform|join(', ') }})</em>{% endif -%} + {%- if pform and pform[0] %} <em>({{ pform|join(', ') }})</em>{% endif -%} </td><td>{% if dep %}<strong>{{ _('Deprecated')}}:</strong>{% endif %} <em>{{ synops|e }}</em></td></tr> {%- endif -%} diff --git a/sphinx/templates/opensearch.xml b/sphinx/themes/basic/opensearch.xml index 03875be4..03875be4 100644 --- a/sphinx/templates/opensearch.xml +++ b/sphinx/themes/basic/opensearch.xml diff --git a/sphinx/themes/basic/page.html b/sphinx/themes/basic/page.html new file mode 100644 index 00000000..17a93016 --- /dev/null +++ b/sphinx/themes/basic/page.html @@ -0,0 +1,4 @@ +{% extends "layout.html" %} +{% block body %} + {{ body }} +{% endblock %} diff --git a/sphinx/templates/search.html b/sphinx/themes/basic/search.html index 545a459b..224d87b8 100644 --- a/sphinx/templates/search.html +++ b/sphinx/themes/basic/search.html @@ -3,6 +3,13 @@ {% set script_files = script_files + ['_static/searchtools.js'] %} {% block body %} <h1 id="search-documentation">{{ _('Search') }}</h1> + <div id="fallback" class="admonition warning"> + <script type="text/javascript">$('#fallback').hide();</script> + <p> + {% trans %}Please activate JavaScript to enable the search + functionality.{% endtrans %} + </p> + </div> <p> {% trans %}From here you can search these documents. Enter your search words into the box below and click "search". Note that the search diff --git a/sphinx/static/default.css b/sphinx/themes/basic/static/basic.css index 005caa1f..ed737d3b 100644 --- a/sphinx/static/default.css +++ b/sphinx/themes/basic/static/basic.css @@ -1,21 +1,9 @@ /** - * Sphinx Doc Design + * Sphinx stylesheet -- basic theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -/* :::: LAYOUT :::: */ - -div.document { - background-color: #1c4e63; -} +/* -- main layout ----------------------------------------------------------- */ div.documentwrapper { float: left; @@ -26,44 +14,14 @@ div.bodywrapper { margin: 0 0 0 230px; } -div.body { - background-color: white; - padding: 0 20px 30px 20px; -} - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - div.clearer { clear: both; } -div.footer { - color: #fff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #fff; - text-decoration: underline; -} +/* -- relbar ---------------------------------------------------------------- */ div.related { - background-color: #133f52; - color: #fff; width: 100%; - line-height: 30px; font-size: 90%; } @@ -86,46 +44,21 @@ div.related li.right { margin-right: 5px; } -div.related a { - color: white; -} - -/* ::: TOC :::: */ -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: white; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: white; -} +/* -- sidebar --------------------------------------------------------------- */ -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: white; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: white; +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; } -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; } div.sphinxsidebar ul { - margin: 10px; - padding: 0; list-style: none; - color: white; } div.sphinxsidebar ul ul, @@ -139,10 +72,6 @@ div.sphinxsidebar ul ul { margin-bottom: 0; } -div.sphinxsidebar a { - color: #98dbcc; -} - div.sphinxsidebar form { margin-top: 10px; } @@ -153,20 +82,12 @@ div.sphinxsidebar input { font-size: 1em; } -/* :::: MODULE CLOUD :::: */ -div.modulecloud { - margin: -5px 10px 5px 10px; - padding: 10px; - line-height: 160%; - border: 1px solid #cbe7e5; - background-color: #f2fbfd; +img { + border: 0; } -div.modulecloud a { - padding: 0 5px 0 5px; -} +/* -- search page ----------------------------------------------------------- */ -/* :::: SEARCH :::: */ ul.search { margin: 10px 0 0 20px; padding: 0; @@ -193,39 +114,7 @@ ul.keywordmatches li.goodmatch a { font-weight: bold; } -/* :::: COMMON FORM STYLES :::: */ - -div.actions { - padding: 5px 10px 5px 10px; - border-top: 1px solid #cbe7e5; - border-bottom: 1px solid #cbe7e5; - background-color: #e0f6f4; -} - -form dl { - color: #333; -} - -form dt { - clear: both; - float: left; - min-width: 110px; - margin-right: 10px; - padding-top: 2px; -} - -input#homepage { - display: none; -} - -div.error { - margin: 5px 20px 0 0; - padding: 5px; - border: 1px solid #d00; - font-weight: bold; -} - -/* :::: INDEX PAGE :::: */ +/* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; @@ -245,7 +134,7 @@ span.linkdescr { font-size: 90%; } -/* :::: INDEX STYLES :::: */ +/* -- general index --------------------------------------------------------- */ table.indextable td { text-align: left; @@ -272,60 +161,9 @@ img.toggler { cursor: pointer; } -form.pfform { - margin: 10px 0 20px 0; -} - -/* :::: GLOBAL STYLES :::: */ - -.docwarning { - background-color: #ffe4e4; - padding: 10px; - margin: 0 -20px 0 -20px; - border-bottom: 1px solid #f66; -} - -p.subhead { - font-weight: bold; - margin-top: 20px; -} - -a { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } +/* -- general body styles --------------------------------------------------- */ a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; visibility: hidden; } @@ -339,16 +177,6 @@ dt:hover > a.headerlink { visibility: visible; } -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - text-align: justify; - line-height: 130%; -} - div.body p.caption { text-align: inherit; } @@ -357,12 +185,6 @@ div.body td { text-align: left; } -ul.fakelist { - list-style: none; - margin: 10px 0 10px 20px; - padding: 0; -} - .field-list ul { padding-left: 1em; } @@ -371,13 +193,12 @@ ul.fakelist { margin-top: 0 !important; } -/* "Footnotes" heading */ p.rubric { margin-top: 30px; font-weight: bold; } -/* Sidebars */ +/* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; @@ -392,10 +213,9 @@ p.sidebar-title { font-weight: bold; } -/* "Topics" */ +/* -- topics ---------------------------------------------------------------- */ div.topic { - background-color: #eee; border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; @@ -407,7 +227,7 @@ p.topic-title { margin-top: 10px; } -/* Admonitions */ +/* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; @@ -423,33 +243,9 @@ div.admonition dl { margin-bottom: 0; } -div.admonition p.admonition-title + p { - display: inline; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; - display: inline; -} - -p.admonition-title:after { - content: ":"; } div.body p.centered { @@ -457,8 +253,11 @@ div.body p.centered { margin-top: 25px; } +/* -- tables ---------------------------------------------------------------- */ + table.docutils { border: 0; + border-collapse: collapse; } table.docutils td, table.docutils th { @@ -477,18 +276,15 @@ table.footnote td, table.footnote th { border: 0 !important; } -.field-list ul { - margin: 0; - padding-left: 1em; +th { + text-align: left; + padding-right: 5px; } -.field-list p { - margin: 0; -} +/* -- other body styles ----------------------------------------------------- */ dl { margin-bottom: 15px; - clear: both; } dd p { @@ -505,12 +301,7 @@ dd { margin-left: 30px; } -.refcount { - color: #060; -} - -dt:target, -.highlight { +dt:target, .highlight { background-color: #fbe54e; } @@ -519,18 +310,40 @@ dl.glossary dt { font-size: 1.1em; } -th { - text-align: left; - padding-right: 5px; +.field-list ul { + margin: 0; + padding-left: 1em; } -pre { +.field-list p { + margin: 0; +} + +.refcount { + color: #060; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; padding: 5px; - background-color: #efc; - color: #333; - border: 1px solid #ac9; - border-left: none; - border-right: none; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa +} + +/* -- code displays --------------------------------------------------------- */ + +pre { overflow: auto; } @@ -549,12 +362,6 @@ table.highlighttable td { padding: 0 0.5em 0 0.5em; } -tt { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - tt.descname { background-color: transparent; font-weight: bold; @@ -570,57 +377,11 @@ tt.xref, a tt { font-weight: bold; } -.footnote:target { background-color: #ffa } - h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -form.comment { - margin: 0; - padding: 10px 30px 10px 30px; - background-color: #eee; -} - -form.comment h3 { - background-color: #326591; - color: white; - margin: -10px -30px 10px -30px; - padding: 5px; - font-size: 1.4em; -} - -form.comment input, -form.comment textarea { - border: 1px solid #ccc; - padding: 2px; - font-family: sans-serif; - font-size: 100%; -} - -form.comment input[type="text"] { - width: 240px; -} - -form.comment textarea { - width: 100%; - height: 200px; - margin-bottom: 10px; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} +/* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; @@ -634,23 +395,19 @@ span.eqno { float: right; } -img.logo { - border: 0; -} +/* -- printout stylesheet --------------------------------------------------- */ -/* :::: PRINT :::: */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0; - width : 100%; + width: 100%; } div.sphinxsidebar, div.related, div.footer, - div#comments div.new-comment-box, #top-link { display: none; } diff --git a/sphinx/static/doctools.js b/sphinx/themes/basic/static/doctools.js index be4bdc88..be4bdc88 100644 --- a/sphinx/static/doctools.js +++ b/sphinx/themes/basic/static/doctools.js diff --git a/sphinx/static/file.png b/sphinx/themes/basic/static/file.png Binary files differindex d18082e3..d18082e3 100644 --- a/sphinx/static/file.png +++ b/sphinx/themes/basic/static/file.png diff --git a/sphinx/static/jquery.js b/sphinx/themes/basic/static/jquery.js index 82b98e1d..82b98e1d 100644 --- a/sphinx/static/jquery.js +++ b/sphinx/themes/basic/static/jquery.js diff --git a/sphinx/static/minus.png b/sphinx/themes/basic/static/minus.png Binary files differindex da1c5620..da1c5620 100644 --- a/sphinx/static/minus.png +++ b/sphinx/themes/basic/static/minus.png diff --git a/sphinx/static/plus.png b/sphinx/themes/basic/static/plus.png Binary files differindex b3cb3742..b3cb3742 100644 --- a/sphinx/static/plus.png +++ b/sphinx/themes/basic/static/plus.png diff --git a/sphinx/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index c4ac17d8..c4ac17d8 100644 --- a/sphinx/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js diff --git a/sphinx/themes/basic/theme.conf b/sphinx/themes/basic/theme.conf new file mode 100644 index 00000000..d1fe6d1f --- /dev/null +++ b/sphinx/themes/basic/theme.conf @@ -0,0 +1,7 @@ +[theme] +inherit = none +stylesheet = basic.css +pygments_style = none + +[options] +nosidebar = false diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t new file mode 100644 index 00000000..ab2aeb0c --- /dev/null +++ b/sphinx/themes/default/static/default.css_t @@ -0,0 +1,234 @@ +/** + * Sphinx stylesheet -- default theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: {{ theme_bodyfont }}; + font-size: 100%; + background-color: {{ theme_footerbgcolor }}; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-color: {{ theme_sidebarbgcolor }}; +} + +div.body { + background-color: {{ theme_bgcolor }}; + color: {{ theme_textcolor }}; + padding: 0 20px 30px 20px; +} + +{%- if theme_rightsidebar|tobool %} +div.bodywrapper { + margin: 0 230px 0 0; +} +{%- endif %} + +div.footer { + color: {{ theme_footertextcolor }}; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: {{ theme_footertextcolor }}; + text-decoration: underline; +} + +div.related { + background-color: {{ theme_relbarbgcolor }}; + line-height: 30px; + color: {{ theme_relbartextcolor }}; +} + +div.related a { + color: {{ theme_relbarlinkcolor }}; +} + +div.sphinxsidebar { + {%- if theme_stickysidebar|tobool %} + top: 30px; + margin: 0; + position: fixed; + overflow: auto; + height: 100%; + {%- endif %} + {%- if theme_rightsidebar|tobool %} + float: right; + {%- if theme_stickysidebar|tobool %} + right: 0; + {%- endif %} + {%- endif %} +} + +{%- if theme_stickysidebar|tobool %} +/* this is nice, but it it leads to hidden headings when jumping + to an anchor */ +/* +div.related { + position: fixed; +} + +div.documentwrapper { + margin-top: 30px; +} +*/ +{%- endif %} + +div.sphinxsidebar h3 { + font-family: {{ theme_headfont }}; + color: {{ theme_sidebartextcolor }}; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar h4 { + font-family: {{ theme_headfont }}; + color: {{ theme_sidebartextcolor }}; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar a { + color: {{ theme_sidebarlinkcolor }}; +} + +div.sphinxsidebar input { + border: 1px solid {{ theme_sidebarlinkcolor }}; + font-family: sans-serif; + font-size: 1em; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: {{ theme_linkcolor }}; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: {{ theme_headfont }}; + background-color: {{ theme_headbgcolor }}; + font-weight: normal; + color: {{ theme_headtextcolor }}; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: {{ theme_headlinkcolor }}; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: {{ theme_headlinkcolor }}; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: {{ theme_codebgcolor }}; + color: {{ theme_codetextcolor }}; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf new file mode 100644 index 00000000..812330f8 --- /dev/null +++ b/sphinx/themes/default/theme.conf @@ -0,0 +1,28 @@ +[theme] +inherit = basic +stylesheet = default.css +pygments_style = sphinx + +[options] +rightsidebar = false +stickysidebar = false + +footerbgcolor = #11303d +footertextcolor = #ffffff +sidebarbgcolor = #1c4e63 +sidebartextcolor = #ffffff +sidebarlinkcolor = #98dbcc +relbarbgcolor = #133f52 +relbartextcolor = #ffffff +relbarlinkcolor = #ffffff +bgcolor = #ffffff +textcolor = #000000 +headbgcolor = #f2f2f2 +headtextcolor = #20435c +headlinkcolor = #c60f0f +linkcolor = #355f7c +codebgcolor = #eeffcc +codetextcolor = #333333 + +bodyfont = sans-serif +headfont = 'Trebuchet MS', sans-serif diff --git a/sphinx/themes/sphinxdoc/layout.html b/sphinx/themes/sphinxdoc/layout.html new file mode 100644 index 00000000..48d2118e --- /dev/null +++ b/sphinx/themes/sphinxdoc/layout.html @@ -0,0 +1,5 @@ +{% extends "basic/layout.html" %} + +{# put the sidebar before the body #} +{% block sidebar1 %}{{ sidebar() }}{% endblock %} +{% block sidebar2 %}{% endblock %} diff --git a/sphinx/static/contents.png b/sphinx/themes/sphinxdoc/static/contents.png Binary files differindex 7fb82154..7fb82154 100644 --- a/sphinx/static/contents.png +++ b/sphinx/themes/sphinxdoc/static/contents.png diff --git a/sphinx/static/navigation.png b/sphinx/themes/sphinxdoc/static/navigation.png Binary files differindex 1081dc14..1081dc14 100644 --- a/sphinx/static/navigation.png +++ b/sphinx/themes/sphinxdoc/static/navigation.png diff --git a/sphinx/static/sphinxdoc.css b/sphinx/themes/sphinxdoc/static/sphinxdoc.css index 999bf48c..1d11e8b9 100644 --- a/sphinx/static/sphinxdoc.css +++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css @@ -1,15 +1,21 @@ /** - * Alternate Sphinx design + * Sphinx stylesheet -- sphinxdoc theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl. */ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + body { - font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif; + font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', + 'Verdana', sans-serif; font-size: 14px; letter-spacing: -0.01em; line-height: 150%; text-align: center; - /*background-color: #AFC1C4; */ background-color: #BFD1D4; color: black; padding: 0; @@ -19,137 +25,8 @@ body { min-width: 740px; } -a { - color: #CA7900; - text-decoration: none; -} - -a:hover { - color: #2491CF; -} - -pre { - font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.95em; - letter-spacing: 0.015em; - padding: 0.5em; - border: 1px solid #ccc; - background-color: #f8f8f8; -} - -td.linenos pre { - padding: 0.5em 0; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -cite, code, tt { - font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.95em; - letter-spacing: 0.01em; -} - -hr { - border: 1px solid #abc; - margin: 2em; -} - -tt { - background-color: #f2f2f2; - border-bottom: 1px solid #ddd; - color: #333; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; - border: 0; -} - -tt.descclassname { - background-color: transparent; - border: 0; -} - -tt.xref { - background-color: transparent; - font-weight: bold; - border: 0; -} - -a tt { - background-color: transparent; - font-weight: bold; - border: 0; - color: #CA7900; -} - -a tt:hover { - color: #2491CF; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.refcount { - color: #060; -} - -dt:target, -.highlight { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -pre { - line-height: 120%; -} - -pre a { - color: inherit; - text-decoration: underline; -} - -.first { - margin-top: 0 !important; +div.documentwrapper { + float: none; } div.document { @@ -159,28 +36,25 @@ div.document { background-repeat: repeat-x; } -/* -div.documentwrapper { - width: 100%; +div.bodywrapper { + margin: 0 240px 0 0; + border-right: 1px solid #ccc; } -*/ -div.clearer { - clear: both; +div.body { + margin: 0; + padding: 0.5em 20px 20px 20px; } -div.related h3 { - display: none; +div.related { + font-size: 1em; } div.related ul { background-image: url(navigation.png); height: 2em; - list-style: none; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; - margin: 0; - padding-left: 10px; } div.related ul li { @@ -206,18 +80,8 @@ div.related ul li a:hover { color: #3CA8E7; } -div.body { - margin: 0; - padding: 0.5em 20px 20px 20px; -} - -div.bodywrapper { - margin: 0 240px 0 0; - border-right: 1px solid #ccc; -} - -div.body a { - text-decoration: underline; +div.sphinxsidebarwrapper { + padding: 0; } div.sphinxsidebar { @@ -225,13 +89,13 @@ div.sphinxsidebar { padding: 0.5em 15px 15px 0; width: 210px; float: right; + font-size: 1em; text-align: left; -/* margin-left: -100%; */ } -div.sphinxsidebar h4, div.sphinxsidebar h3 { +div.sphinxsidebar h3, div.sphinxsidebar h4 { margin: 1em 0 0.5em 0; - font-size: 0.9em; + font-size: 1em; padding: 0.1em 0 0.1em 0.5em; color: white; border: 1px solid #86989B; @@ -245,55 +109,45 @@ div.sphinxsidebar h3 a { div.sphinxsidebar ul { padding-left: 1.5em; margin-top: 7px; - list-style: none; padding: 0; line-height: 130%; } div.sphinxsidebar ul ul { - list-style: square; margin-left: 20px; } -p { - margin: 0.8em 0 0.5em 0; +div.footer { + background-color: #E3EFF1; + color: #86989B; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; } -p.rubric { - font-weight: bold; +div.footer a { + color: #86989B; + text-decoration: underline; } -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} +/* -- body styles ----------------------------------------------------------- */ -div.quotebar { - background-color: #f8f8f8; - max-width: 250px; - float: right; - padding: 2px 7px; - border: 1px solid #ccc; +p { + margin: 0.8em 0 0.5em 0; } -p.sidebar-title { - font-weight: bold; +a { + color: #CA7900; + text-decoration: none; } -div.topic { - background-color: #f8f8f8; - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; +a:hover { + color: #2491CF; } -p.topic-title { - font-size: 1.1em; - font-weight: bold; +div.body a { + text-decoration: underline; } h1 { @@ -336,67 +190,97 @@ h5 a.anchor:hover, h6 a.anchor:hover { background-color: #eee; } -table { - border-collapse: collapse; - margin: 0 -0.5em 0 -0.5em; +a.headerlink { + color: #c60f0f!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; } -table td, table th { - padding: 0.2em 0.5em 0.2em 0.5em; +a.headerlink:hover { + background-color: #ccc; + color: white!important; } -div.footer { - background-color: #E3EFF1; - color: #86989B; - padding: 3px 8px 3px 0; - clear: both; - font-size: 0.8em; - text-align: right; +cite, code, tt { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.01em; } -div.footer a { - color: #86989B; - text-decoration: underline; +tt { + background-color: #f2f2f2; + border-bottom: 1px solid #ddd; + color: #333; } -div.pagination { - margin-top: 2em; - padding-top: 0.5em; - border-top: 1px solid black; - text-align: center; +tt.descname, tt.descclassname, tt.xref { + border: 0; } -div.sphinxsidebar ul.toc { - margin: 1em 0 1em 0; - padding: 0 0 0 0.5em; - list-style: none; +hr { + border: 1px solid #abc; + margin: 2em; } -div.sphinxsidebar ul.toc li { - margin: 0.5em 0 0.5em 0; - font-size: 0.9em; - line-height: 130%; +a tt { + border: 0; + color: #CA7900; } -div.sphinxsidebar ul.toc li p { - margin: 0; - padding: 0; +a tt:hover { + color: #2491CF; } -div.sphinxsidebar ul.toc ul { - margin: 0.2em 0 0.2em 0; - padding: 0 0 0 1.8em; +pre { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.5em; + border: 1px solid #ccc; + background-color: #f8f8f8; } -div.sphinxsidebar ul.toc ul li { - padding: 0; +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #f8f8f8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #ccc; +} + +div.topic { + background-color: #f8f8f8; +} + +table { + border-collapse: collapse; + margin: 0 -0.5em 0 -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; } div.admonition, div.warning { font-size: 0.9em; - margin: 1em 0 0 0; + margin: 1em 0 1em 0; border: 1px solid #86989B; background-color: #f7f7f7; + padding: 0; } div.admonition p, div.warning p { @@ -441,117 +325,3 @@ div.versioninfo { line-height: 1.3em; font-size: 0.9em; } - - -a.headerlink { - color: #c60f0f!important; - font-size: 1em; - margin-left: 6px; - padding: 0 4px 0 4px; - text-decoration: none!important; - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -a.headerlink:hover { - background-color: #ccc; - color: white!important; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -form.pfform { - margin: 10px 0 20px 0; -} - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -img.math { - vertical-align: center; -} - -div.math { - text-align: center; -} - -span.eqno { - float: right; -} - -img.logo { - border: 0; -} diff --git a/sphinx/themes/sphinxdoc/theme.conf b/sphinx/themes/sphinxdoc/theme.conf new file mode 100644 index 00000000..9a22fefd --- /dev/null +++ b/sphinx/themes/sphinxdoc/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = sphinxdoc.css +pygments_style = friendly diff --git a/sphinx/static/traditional.css b/sphinx/themes/traditional/static/traditional.css index 8c224c07..8c224c07 100644 --- a/sphinx/static/traditional.css +++ b/sphinx/themes/traditional/static/traditional.css diff --git a/sphinx/themes/traditional/theme.conf b/sphinx/themes/traditional/theme.conf new file mode 100644 index 00000000..02b77833 --- /dev/null +++ b/sphinx/themes/traditional/theme.conf @@ -0,0 +1,3 @@ +[theme] +inherit = basic +stylesheet = traditional.css diff --git a/sphinx/theming.py b/sphinx/theming.py new file mode 100644 index 00000000..77c3137c --- /dev/null +++ b/sphinx/theming.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" + sphinx.theming + ~~~~~~~~~~~~~~ + + Theming support for HTML builders. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import shutil +import zipfile +import tempfile +import ConfigParser +from os import path + +from sphinx.errors import ThemeError + + +NODEFAULT = object() +THEMECONF = 'theme.conf' + +class Theme(object): + """ + Represents the theme chosen in the configuration. + """ + themes = {} + + @classmethod + def init_themes(cls, builder): + """Search all theme paths for available themes.""" + cls.themepath = list(builder.config.html_theme_path) + cls.themepath.append( + path.join(path.abspath(path.dirname(__file__)), 'themes')) + + for themedir in cls.themepath[::-1]: + themedir = path.join(builder.confdir, themedir) + if not path.isdir(themedir): + continue + for theme in os.listdir(themedir): + if theme.lower().endswith('.zip'): + try: + zfile = zipfile.ZipFile(path.join(themedir, theme)) + if THEMECONF not in zfile.namelist(): + continue + tname = theme[:-4] + tinfo = zfile + except Exception: + builder.warn('file %r on theme path is not a valid ' + 'zipfile or contains no theme' % theme) + continue + else: + if not path.isfile(path.join(themedir, theme, THEMECONF)): + continue + tname = theme + tinfo = None + cls.themes[tname] = (path.join(themedir, theme), tinfo) + + def __init__(self, name): + if name not in self.themes: + raise ThemeError('no theme named %r found ' + '(missing theme.conf?)' % name) + self.name = name + + tdir, tinfo = self.themes[name] + if tinfo is None: + # already a directory, do nothing + self.themedir = tdir + self.themedir_created = False + else: + # extract the theme to a temp directory + self.themedir = tempfile.mkdtemp('sxt') + self.themedir_created = True + for name in tinfo.namelist(): + if name.endswith('/'): continue + dirname = path.dirname(name) + if not path.isdir(path.join(self.themedir, dirname)): + os.makedirs(path.join(self.themedir, dirname)) + fp = open(path.join(self.themedir, name), 'w') + fp.write(tinfo.read(name)) + fp.close() + + self.themeconf = ConfigParser.RawConfigParser() + self.themeconf.read(path.join(self.themedir, THEMECONF)) + + try: + inherit = self.themeconf.get('theme', 'inherit') + except ConfigParser.NoOptionError: + raise ThemeError('theme %r doesn\'t have "inherit" setting' % name) + if inherit == 'none': + self.base = None + elif inherit not in self.themes: + raise ThemeError('no theme named %r found, inherited by %r' % + (inherit, name)) + else: + self.base = Theme(inherit) + + def get_confstr(self, section, name, default=NODEFAULT): + """ + Return the value for a theme configuration setting, searching the + base theme chain. + """ + try: + return self.themeconf.get(section, name) + except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + if self.base is not None: + return self.base.get_confstr(section, name, default) + if default is NODEFAULT: + raise ThemeError('setting %s.%s occurs in none of the ' + 'searched theme configs' % (section, name)) + else: + return default + + def get_options(self, overrides): + """ + Return a dictionary of theme options and their values. + """ + chain = [self.themeconf] + base = self.base + while base is not None: + chain.append(base.themeconf) + base = base.base + options = {} + for conf in reversed(chain): + try: + options.update(conf.items('options')) + except ConfigParser.NoSectionError: + pass + for option, value in overrides.iteritems(): + if option not in options: + raise ThemeError('unsupported theme option %r given' % option) + options[option] = value + return options + + def get_dirchain(self): + """ + Return a list of theme directories, beginning with this theme's, + then the base theme's, then that one's base theme's, etc. + """ + chain = [self.themedir] + base = self.base + while base is not None: + chain.append(base.themedir) + base = base.base + return chain + + def cleanup(self): + """ + Remove temporary directories. + """ + if self.themedir_created: + try: + shutil.rmtree(self.themedir) + except Exception: + pass + if self.base: + self.base.cleanup() diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 78e9e14a..c03016a4 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -13,8 +13,11 @@ import os import re import sys import time +import types +import shutil import fnmatch import tempfile +import posixpath import traceback from os import path @@ -22,7 +25,7 @@ from os import path # Generally useful regular expressions. ws_re = re.compile(r'\s+') caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$') - +url_re = re.compile(r'(?P<schema>.+)://.*') # SEP separates path elements in the canonical file names # @@ -50,6 +53,11 @@ def relative_uri(base, to): return ('..' + SEP) * (len(b2)-1) + SEP.join(t2) +def docname_join(basedocname, docname): + return posixpath.normpath( + posixpath.join('/' + basedocname, '..', docname))[1:] + + def ensuredir(path): """Ensure that a path exists.""" try: @@ -276,9 +284,11 @@ def nested_parse_with_titles(state, content, node): surrounding_section_level = state.memo.section_level state.memo.title_styles = [] state.memo.section_level = 0 - state.nested_parse(content, 0, node, match_titles=1) - state.memo.title_styles = surrounding_title_styles - state.memo.section_level = surrounding_section_level + try: + return state.nested_parse(content, 0, node, match_titles=1) + finally: + state.memo.title_styles = surrounding_title_styles + state.memo.section_level = surrounding_section_level def ustrftime(format, *args): @@ -286,6 +296,93 @@ def ustrftime(format, *args): return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8') +class Tee(object): + """ + File-like object writing to two streams. + """ + def __init__(self, stream1, stream2): + self.stream1 = stream1 + self.stream2 = stream2 + + def write(self, text): + self.stream1.write(text) + self.stream2.write(text) + + +class FilenameUniqDict(dict): + """ + A dictionary that automatically generates unique names for its keys, + interpreted as filenames, and keeps track of a set of docnames they + appear in. Used for images and downloadable files in the environment. + """ + def __init__(self): + self._existing = set() + + def add_file(self, docname, newfile): + if newfile in self: + self[newfile][0].add(docname) + return self[newfile][1] + uniquename = path.basename(newfile) + base, ext = path.splitext(uniquename) + i = 0 + while uniquename in self._existing: + i += 1 + uniquename = '%s%s%s' % (base, i, ext) + self[newfile] = (set([docname]), uniquename) + self._existing.add(uniquename) + return uniquename + + def purge_doc(self, docname): + for filename, (docs, _) in self.items(): + docs.discard(docname) + #if not docs: + # del self[filename] + # self._existing.discard(filename) + + def __getstate__(self): + return self._existing + + def __setstate__(self, state): + self._existing = state + + +def parselinenos(spec, total): + """ + Parse a line number spec (such as "1,2,4-6") and return a list of + wanted line numbers. + """ + items = list() + parts = spec.split(',') + for part in parts: + try: + begend = part.strip().split('-') + if len(begend) > 2: + raise ValueError + if len(begend) == 1: + items.append(int(begend[0])-1) + else: + start = (begend[0] == '') and 0 or int(begend[0])-1 + end = (begend[1] == '') and total or int(begend[1]) + items.extend(xrange(start, end)) + except Exception, err: + raise ValueError('invalid line number spec: %r' % spec) + return items + + +def force_decode(string, encoding): + if isinstance(string, str): + if encoding: + string = string.decode(encoding) + else: + try: + # try decoding with utf-8, should only work for real UTF-8 + string = string.decode('utf-8') + except UnicodeError: + # last resort -- can't fail + string = string.decode('latin1') + return string + + def movefile(source, dest): # move a file, removing the destination if it exists if os.path.exists(dest): @@ -294,3 +391,60 @@ def movefile(source, dest): except OSError: pass os.rename(source, dest) + + +def copy_static_entry(source, target, builder, context={}): + if path.isfile(source): + if source.lower().endswith('_t'): + # templated! + fsrc = open(source, 'rb') + fdst = open(target[:-2], 'wb') + fdst.write(builder.templates.render_string(fsrc.read(), context)) + fsrc.close() + fdst.close() + else: + shutil.copyfile(source, target) + elif path.isdir(source): + if filename in builder.config.exclude_dirnames: + return + if path.exists(target): + shutil.rmtree(target) + shutil.copytree(source, target) + + +# monkey-patch Node.traverse to get more speed +# traverse() is called so many times during a build that it saves +# on average 20-25% overall build time! + +def _all_traverse(self): + """Version of Node.traverse() that doesn't need a condition.""" + result = [] + result.append(self) + for child in self.children: + result.extend(child._all_traverse()) + return result + +def _fast_traverse(self, cls): + """Version of Node.traverse() that only supports instance checks.""" + result = [] + if isinstance(self, cls): + result.append(self) + for child in self.children: + result.extend(child._fast_traverse(cls)) + return result + +def _new_traverse(self, condition=None, + include_self=1, descend=1, siblings=0, ascend=0): + if include_self and descend and not siblings and not ascend: + if condition is None: + return self._all_traverse() + elif isinstance(condition, (types.ClassType, type)): + return self._fast_traverse(condition) + return self._old_traverse(condition, include_self, + descend, siblings, ascend) + +import docutils.nodes +docutils.nodes.Node._old_traverse = docutils.nodes.Node.traverse +docutils.nodes.Node._all_traverse = _all_traverse +docutils.nodes.Node._fast_traverse = _fast_traverse +docutils.nodes.Node.traverse = _new_traverse diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index 4d5e1996..c00f9d4e 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -11,7 +11,6 @@ from docutils import nodes - # function missing in 0.5 SVN def make_admonition(node_class, name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): @@ -27,7 +26,7 @@ def make_admonition(node_class, name, arguments, options, content, lineno, textnodes, messages = state.inline_text(title_text, lineno) admonition_node += nodes.title(title_text, '', *textnodes) admonition_node += messages - if options.has_key('class'): + if 'class' in options: classes = options['class'] else: classes = ['admonition-' + nodes.make_id(title_text)] @@ -35,3 +34,65 @@ def make_admonition(node_class, name, arguments, options, content, lineno, state.nested_parse(content, content_offset, admonition_node) return [admonition_node] + +# support the class-style Directive interface even when using docutils 0.4 + +try: + from docutils.parsers.rst import Directive + +except ImportError: + class Directive(object): + """ + Fake Directive class to allow Sphinx directives to be written in + class style. + """ + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = None + has_content = False + + def __init__(self, name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + self.name = name + self.arguments = arguments + self.options = options + self.content = content + self.lineno = lineno + self.content_offset = content_offset + self.block_text = block_text + self.state = state + self.state_machine = state_machine + + def run(self): + raise NotImplementedError('Must override run() is subclass.') + + def directive_dwim(obj): + """ + Return something usable with register_directive(), regardless if + class or function. For that, we need to convert classes to a + function for docutils 0.4. + """ + if isinstance(obj, type) and issubclass(obj, Directive): + def _class_directive(name, arguments, options, content, + lineno, content_offset, block_text, + state, state_machine): + return obj(name, arguments, options, content, + lineno, content_offset, block_text, + state, state_machine).run() + _class_directive.options = obj.option_spec + _class_directive.content = obj.has_content + _class_directive.arguments = (obj.required_arguments, + obj.optional_arguments, + obj.final_argument_whitespace) + return _class_directive + return obj + +else: + def directive_dwim(obj): + """ + Return something usable with register_directive(), regardless if + class or function. Nothing to do here, because docutils 0.5 takes + care of converting functions itself. + """ + return obj diff --git a/sphinx/util/console.py b/sphinx/util/console.py index 724dee10..083fc6f4 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -16,25 +16,26 @@ codes = {} def get_terminal_width(): """Borrowed from the py lib.""" try: - import os, termios, fcntl, struct - call = fcntl.ioctl(0, termios.TIOCGWINSZ, "\000"*8) - height, width = struct.unpack("hhhh", call)[:2] + import termios, fcntl, struct + call = fcntl.ioctl(0, termios.TIOCGWINSZ, + struct.pack('hhhh', 0, 0, 0, 0)) + height, width = struct.unpack('hhhh', call)[:2] terminal_width = width except (SystemExit, KeyboardInterrupt): raise except: # FALLBACK - terminal_width = int(os.environ.get('COLUMNS', 80))-1 + terminal_width = int(os.environ.get('COLUMNS', 80)) - 1 return terminal_width _tw = get_terminal_width() -def print_and_backspace(text, func): +def term_width_line(text): if not codes: # if no coloring, don't output fancy backspaces - func(text) + return text + '\n' else: - func(text.ljust(_tw) + _tw * "\b") + return text.ljust(_tw) + '\r' def color_terminal(): if 'COLORTERM' in os.environ: diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py new file mode 100644 index 00000000..ea03340a --- /dev/null +++ b/sphinx/util/docstrings.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.docstrings + ~~~~~~~~~~~~~~~~~~~~~~ + + Utilities for docstring processing. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys + + +def prepare_docstring(s): + """ + Convert a docstring into lines of parseable reST. Return it as a list of + lines usable for inserting into a docutils ViewList (used as argument + of nested_parse().) An empty line is added to act as a separator between + this docstring and following content. + """ + lines = s.expandtabs().splitlines() + # Find minimum indentation of any non-blank lines after first line. + margin = sys.maxint + for line in lines[1:]: + content = len(line.lstrip()) + if content: + indent = len(line) - content + margin = min(margin, indent) + # Remove indentation. + if lines: + lines[0] = lines[0].lstrip() + if margin < sys.maxint: + for i in range(1, len(lines)): lines[i] = lines[i][margin:] + # Remove any leading blank lines. + while lines and not lines[0]: + lines.pop(0) + # make sure there is an empty line at the end + if lines and lines[-1]: + lines.append('') + return lines + + +def prepare_commentdoc(s): + """ + Extract documentation comment lines (starting with #:) and return them as a + list of lines. Returns an empty list if there is no documentation. + """ + result = [] + lines = [line.strip() for line in s.expandtabs().splitlines()] + for line in lines: + if line.startswith('#:'): + line = line[2:] + # the first space after the comment is ignored + if line and line[0] == ' ': + line = line[1:] + result.append(line) + if result and result[-1]: + result.append('') + return result diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py index 2eb4619e..8c760b68 100644 --- a/sphinx/util/jsdump.py +++ b/sphinx/util/jsdump.py @@ -6,7 +6,7 @@ This module implements a simple JavaScript serializer. Uses the basestring encode function from simplejson by Bob Ippolito. - :copyright: Copyright 2008 by the Sphinx team, see AUTHORS. + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index 42f20f36..75888ea4 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -162,7 +162,8 @@ def educateQuotes(s): """ # Special case if the very first character is a quote - # followed by punctuation at a non-word-break. Close the quotes by brute force: + # followed by punctuation at a non-word-break. Close the quotes + # by brute force: s = single_quote_start_re.sub("’", s) s = double_quote_start_re.sub("”", s) @@ -200,7 +201,8 @@ def educateQuotesLatex(s, dquotes=("``", "''")): """ # Special case if the very first character is a quote - # followed by punctuation at a non-word-break. Close the quotes by brute force: + # followed by punctuation at a non-word-break. Close the quotes + # by brute force: s = single_quote_start_re.sub("\x04", s) s = double_quote_start_re.sub("\x02", s) @@ -300,4 +302,5 @@ __author__ = "Chad Miller <smartypantspy@chad.org>" __version__ = "1.5_1.5: Sat, 13 Aug 2005 15:50:24 -0400" __url__ = "http://wiki.chad.org/SmartyPantsPy" __description__ = \ - "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom" + "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries" \ + " in pyblosxom" diff --git a/sphinx/util/stemmer.py b/sphinx/util/stemmer.py index 7eeb77b2..10ce9065 100644 --- a/sphinx/util/stemmer.py +++ b/sphinx/util/stemmer.py @@ -111,14 +111,16 @@ class PorterStemmer(object): return self.cons(j) def cvc(self, i): - """cvc(i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant + """cvc(i) is TRUE <=> i-2,i-1,i has the form + consonant - vowel - consonant and also if the second c is not w,x or y. this is used when trying to restore an e at the end of a short e.g. cav(e), lov(e), hop(e), crim(e), but snow, box, tray. """ - if i < (self.k0 + 2) or not self.cons(i) or self.cons(i-1) or not self.cons(i-2): + if i < (self.k0 + 2) or not self.cons(i) or self.cons(i-1) \ + or not self.cons(i-2): return 0 ch = self.b[i] if ch == 'w' or ch == 'x' or ch == 'y': @@ -138,7 +140,8 @@ class PorterStemmer(object): return 1 def setto(self, s): - """setto(s) sets (j+1),...k to the characters in the string s, readjusting k.""" + """setto(s) sets (j+1),...k to the characters in the string s, + readjusting k.""" length = len(s) self.b = self.b[:self.j+1] + s + self.b[self.j+length+1:] self.k = self.j + length @@ -193,7 +196,8 @@ class PorterStemmer(object): self.setto("e") def step1c(self): - """step1c() turns terminal y to i when there is another vowel in the stem.""" + """step1c() turns terminal y to i when there is another vowel in + the stem.""" if (self.ends("y") and self.vowelinstem()): self.b = self.b[:self.k] + 'i' + self.b[self.k+1:] @@ -236,7 +240,8 @@ class PorterStemmer(object): # To match the published algorithm, delete this phrase def step3(self): - """step3() dels with -ic-, -full, -ness etc. similar strategy to step2.""" + """step3() dels with -ic-, -full, -ness etc. similar strategy + to step2.""" if self.b[self.k] == 'e': if self.ends("icate"): self.r("ic") elif self.ends("ative"): self.r("") diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py new file mode 100644 index 00000000..c08e5e5a --- /dev/null +++ b/sphinx/util/tags.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.tags + ~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import warnings +# jinja2.sandbox imports the sets module on purpose +warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, + module='jinja2.sandbox') + +# (ab)use the Jinja parser for parsing our boolean expressions +from jinja2 import nodes +from jinja2.parser import Parser +from jinja2.environment import Environment + +env = Environment() + + +class BooleanParser(Parser): + """ + Only allow condition exprs and/or/not operations. + """ + + def parse_compare(self): + token = self.stream.current + if token.type == 'name': + if token.value in ('true', 'false', 'True', 'False'): + node = nodes.Const(token.value in ('true', 'True'), + lineno=token.lineno) + elif token.value in ('none', 'None'): + node = nodes.Const(None, lineno=token.lineno) + else: + node = nodes.Name(token.value, 'load', lineno=token.lineno) + self.stream.next() + elif token.type == 'lparen': + self.stream.next() + node = self.parse_expression() + self.stream.expect('rparen') + else: + self.fail("unexpected token '%s'" % (token,), token.lineno) + return node + + +class Tags(object): + def __init__(self, tags=None): + self.tags = dict.fromkeys(tags or [], True) + + def has(self, tag): + return tag in self.tags + + __contains__ = has + + def __iter__(self): + return iter(self.tags) + + def add(self, tag): + self.tags[tag] = True + + def remove(self, tag): + self.tags.pop(tag, None) + + def eval_condition(self, condition): + # exceptions are handled by the caller + parser = BooleanParser(env, condition, state='variable') + expr = parser.parse_expression() + if not parser.stream.eos: + raise ValueError('chunk after expression') + + def eval_node(node): + if isinstance(node, nodes.CondExpr): + if eval_node(node.test): + return eval_node(node.expr1) + else: + return eval_node(node.expr2) + elif isinstance(node, nodes.And): + return eval_node(node.left) and eval_node(node.right) + elif isinstance(node, nodes.Or): + return eval_node(node.left) or eval_node(node.right) + elif isinstance(node, nodes.Not): + return not eval_node(node.node) + elif isinstance(node, nodes.Name): + return self.tags.get(node.name, False) + else: + raise ValueError('invalid node, check parsing') + + return eval_node(expr) diff --git a/sphinx/writers/__init__.py b/sphinx/writers/__init__.py new file mode 100644 index 00000000..7bb8cad9 --- /dev/null +++ b/sphinx/writers/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" + sphinx.writers + ~~~~~~~~~~~~~~ + + Custom docutils writers. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" diff --git a/sphinx/htmlwriter.py b/sphinx/writers/html.py index f983aa76..f600e8bd 100644 --- a/sphinx/htmlwriter.py +++ b/sphinx/writers/html.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.htmlwriter - ~~~~~~~~~~~~~~~~~ + sphinx.writers.html + ~~~~~~~~~~~~~~~~~~~ docutils writers handling Sphinx' custom nodes. @@ -17,7 +17,6 @@ from docutils import nodes from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator from sphinx.locale import admonitionlabels, versionlabels -from sphinx.highlighting import PygmentsBridge from sphinx.util.smartypants import sphinx_smarty_pants try: @@ -25,6 +24,7 @@ try: except ImportError: Image = None + class HTMLWriter(Writer): def __init__(self, builder): Writer.__init__(self) @@ -52,12 +52,13 @@ class HTMLTranslator(BaseTranslator): def __init__(self, builder, *args, **kwds): BaseTranslator.__init__(self, *args, **kwds) - self.highlighter = PygmentsBridge('html', builder.config.pygments_style) + self.highlighter = builder.highlighter self.no_smarty = 0 self.builder = builder self.highlightlang = builder.config.highlight_language self.highlightlinenothreshold = sys.maxint self.protect_literal_text = 0 + self.add_permalinks = builder.config.html_add_permalinks def visit_desc(self, node): self.body.append(self.starttag(node, 'dl', CLASS=node['desctype'])) @@ -68,13 +69,13 @@ class HTMLTranslator(BaseTranslator): # the id is set automatically self.body.append(self.starttag(node, 'dt')) # anchor for per-desc interactive data - if node.parent['desctype'] != 'describe' and node['ids'] and node['first']: + if node.parent['desctype'] != 'describe' \ + and node['ids'] and node['first']: self.body.append('<!--[%s]-->' % node['ids'][0]) - if node.parent['desctype'] in ('class', 'exception'): - self.body.append('%s ' % node.parent['desctype']) def depart_desc_signature(self, node): - if node['ids'] and self.builder.add_definition_links: - self.body.append(u'<a class="headerlink" href="#%s" ' % node['ids'][0] + + if node['ids'] and self.add_permalinks and self.builder.add_permalinks: + self.body.append(u'<a class="headerlink" href="#%s" ' + % node['ids'][0] + u'title="%s">\u00B6</a>' % _('Permalink to this definition')) self.body.append('</dt>\n') @@ -89,6 +90,11 @@ class HTMLTranslator(BaseTranslator): def depart_desc_type(self, node): pass + def visit_desc_returns(self, node): + self.body.append(' → ') + def depart_desc_returns(self, node): + pass + def visit_desc_name(self, node): self.body.append(self.starttag(node, 'tt', '', CLASS='descname')) def depart_desc_name(self, node): @@ -152,6 +158,8 @@ class HTMLTranslator(BaseTranslator): return self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \ starttag[2:] + if node.hasattr('secnumber'): + self.body.append('%s. ' % '.'.join(map(str, node['secnumber']))) # overwritten -- we don't want source comments to show up in the HTML def visit_comment(self, node): @@ -170,6 +178,17 @@ class HTMLTranslator(BaseTranslator): def depart_seealso(self, node): self.depart_admonition(node) + def add_secnumber(self, node): + if node.hasattr('secnumber'): + self.body.append('.'.join(map(str, node['secnumber'])) + '. ') + elif isinstance(node.parent, nodes.section): + anchorname = '#' + node.parent['ids'][0] + if anchorname not in self.builder.secnumbers: + anchorname = '' # try first heading which has no anchor + if anchorname in self.builder.secnumbers: + numbers = self.builder.secnumbers[anchorname] + self.body.append('.'.join(map(str, numbers)) + '. ') + # overwritten for docutils 0.4 if hasattr(BaseTranslator, 'start_tag_with_title'): def visit_section(self, node): @@ -180,6 +199,11 @@ class HTMLTranslator(BaseTranslator): def visit_title(self, node): # don't move the id attribute inside the <h> tag BaseTranslator.visit_title(self, node, move_ids=0) + self.add_secnumber(node) + else: + def visit_title(self, node): + BaseTranslator.visit_title(self, node) + self.add_secnumber(node) # overwritten def visit_literal_block(self, node): @@ -187,14 +211,17 @@ class HTMLTranslator(BaseTranslator): # most probably a parsed-literal block -- don't highlight return BaseTranslator.visit_literal_block(self, node) lang = self.highlightlang - linenos = node.rawsource.count('\n') >= self.highlightlinenothreshold - 1 + linenos = node.rawsource.count('\n') >= \ + self.highlightlinenothreshold - 1 if node.has_key('language'): # code-block directives lang = node['language'] if node.has_key('linenos'): linenos = node['linenos'] - highlighted = self.highlighter.highlight_block(node.rawsource, lang, linenos) - starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s' % lang) + highlighted = self.highlighter.highlight_block(node.rawsource, + lang, linenos) + starttag = self.starttag(node, 'div', suffix='', + CLASS='highlight-%s' % lang) self.body.append(starttag + highlighted + '</div>\n') raise nodes.SkipNode @@ -206,7 +233,8 @@ class HTMLTranslator(BaseTranslator): if len(node.children) == 1 and \ node.children[0] in ('None', 'True', 'False'): node['classes'].append('xref') - self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal')) + self.body.append(self.starttag(node, 'tt', '', + CLASS='docutils literal')) self.protect_literal_text += 1 def depart_literal(self, node): self.protect_literal_text -= 1 @@ -238,7 +266,8 @@ class HTMLTranslator(BaseTranslator): pass def visit_centered(self, node): - self.body.append(self.starttag(node, 'p', CLASS="centered") + '<strong>') + self.body.append(self.starttag(node, 'p', CLASS="centered") + + '<strong>') def depart_centered(self, node): self.body.append('</strong></p>') @@ -253,6 +282,16 @@ class HTMLTranslator(BaseTranslator): def depart_highlightlang(self, node): pass + def visit_download_reference(self, node): + if node.hasattr('filename'): + self.body.append('<a href="%s">' % posixpath.join( + self.builder.dlpath, node['filename'])) + self.context.append('</a>') + else: + self.context.append('') + def depart_download_reference(self, node): + self.body.append(self.context.pop()) + # overwritten def visit_image(self, node): olduri = node['uri'] @@ -261,15 +300,33 @@ class HTMLTranslator(BaseTranslator): node['uri'] = posixpath.join(self.builder.imgpath, self.builder.images[olduri]) + if node['uri'].lower().endswith('svg') or \ + node['uri'].lower().endswith('svgz'): + atts = {'data': node['uri'], 'type': 'image/svg+xml'} + if 'width' in node: + atts['width'] = node['width'] + if 'height' in node: + atts['height'] = node['height'] + if 'align' in node: + self.body.append('<div align="%s" class="align-%s">' % + (node['align'], node['align'])) + self.context.append('</div>\n') + else: + self.context.append('') + embatts = atts.copy() + embatts['src'] = embatts.pop('data') + self.body.append(self.starttag(node, 'object', '', **atts)) + self.body.append(self.emptytag(node, 'embed', '', **embatts)) + self.body.append('</object>\n') + return + if node.has_key('scale'): if Image and not (node.has_key('width') and node.has_key('height')): try: - im = Image.open(os.path.join(self.builder.srcdir, - olduri)) + im = Image.open(os.path.join(self.builder.srcdir, olduri)) except (IOError, # Source image can't be found or opened UnicodeError): # PIL doesn't like Unicode paths. - print olduri pass else: if not node.has_key('width'): @@ -305,6 +362,16 @@ class HTMLTranslator(BaseTranslator): def depart_module(self, node): pass + def visit_hlist(self, node): + self.body.append('<table class="hlist"><tr>') + def depart_hlist(self, node): + self.body.append('</tr></table>\n') + + def visit_hlistcol(self, node): + self.body.append('<td>') + def depart_hlistcol(self, node): + self.body.append('</td>') + def bulk_text_processor(self, text): return text @@ -386,9 +453,17 @@ class HTMLTranslator(BaseTranslator): def depart_literal_emphasis(self, node): return self.depart_emphasis(node) + def visit_abbreviation(self, node): + attrs = {} + if node.hasattr('explanation'): + attrs['title'] = node['explanation'] + self.body.append(self.starttag(node, 'abbr', **attrs)) + def depart_abbreviation(self, node): + self.body.append('</abbr>') + def depart_title(self, node): close_tag = self.context[-1] - if self.builder.add_header_links and \ + if self.add_permalinks and self.builder.add_permalinks and \ (close_tag.startswith('</h') or close_tag.startswith('</a></h')) and \ node.parent.hasattr('ids') and node.parent['ids']: diff --git a/sphinx/latexwriter.py b/sphinx/writers/latex.py index 88b1a50b..37281fa9 100644 --- a/sphinx/latexwriter.py +++ b/sphinx/writers/latex.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.latexwriter - ~~~~~~~~~~~~~~~~~~ + sphinx.writers.latex + ~~~~~~~~~~~~~~~~~~~~ Custom docutils writer for LaTeX. @@ -21,8 +21,8 @@ from docutils.writers.latex2e import Babel from sphinx import addnodes from sphinx import highlighting +from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, versionlabels -from sphinx.application import SphinxError from sphinx.util import ustrftime from sphinx.util.texescape import tex_escape_map from sphinx.util.smartypants import educateQuotesLatex @@ -98,7 +98,7 @@ class LaTeXWriter(writers.Writer): class ExtBabel(Babel): def get_shorthandoff(self): shortlang = self.language.split('_')[0] - if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl'): + if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl', 'it'): return '\\shorthandoff{"}' return '' @@ -119,7 +119,8 @@ class Table(object): class Desc(object): def __init__(self, node): self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe') - self.type = self.cls = self.name = self.params = self.annotation = '' + self.type = self.cls = self.name = self.params = \ + self.annotation = self.returns = '' self.count = 0 @@ -173,7 +174,8 @@ class LaTeXTranslator(nodes.NodeVisitor): 'pointsize': builder.config.latex_font_size, # if empty, the title is set to the first section title 'title': document.settings.title, - 'date': ustrftime(builder.config.today_fmt or _('%B %d, %Y')), + 'date': ustrftime(builder.config.today_fmt + or _('%B %d, %Y')), 'release': builder.config.release, 'author': document.settings.author, 'releasename': _('Release'), @@ -214,6 +216,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.written_ids = set() self.footnotestack = [] self.curfilestack = [] + self.handled_abbrs = set() if self.elements['docclass'] == 'manual': if builder.config.latex_use_parts: self.top_sectionlevel = 0 @@ -230,6 +233,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.this_is_the_title = 1 self.literal_whitespace = 0 self.no_contractions = 0 + self.compact_list = 0 def astext(self): return (HEADER % self.elements + self.highlighter.get_stylesheet() + @@ -237,7 +241,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_document(self, node): self.footnotestack.append(self.collect_footnotes(node)) - self.curfilestack.append(node['file']) + self.curfilestack.append(node.get('docname', '')) if self.first_document == 1: # the first document is all the regular content ... self.body.append(BEGIN_DOC % self.elements) @@ -246,6 +250,8 @@ class LaTeXTranslator(nodes.NodeVisitor): # ... and all others are the appendices self.body.append('\n\\appendix\n') self.first_document = -1 + if 'docname' in node: + self.body.append('\\hypertarget{--doc-%s}{}' % node['docname']) # "- 1" because the level is increased before the title is visited self.sectionlevel = self.top_sectionlevel - 1 def depart_document(self, node): @@ -258,7 +264,8 @@ class LaTeXTranslator(nodes.NodeVisitor): for bi in self.bibitems: # cite_key: underscores must not be escaped cite_key = bi[0].replace(r"\_", "_") - self.body.append('\\bibitem[%s]{%s}{\hypertarget{%s}{} %s}\n' % (bi[0], cite_key, cite_key.lower(), bi[1])) + self.body.append('\\bibitem[%s]{%s}{\hypertarget{%s}{} %s}\n' % + (bi[0], cite_key, cite_key.lower(), bi[1])) self.body.append('\\end{thebibliography}\n') self.bibitems = [] @@ -268,7 +275,9 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n\\resetcurrentobjects\n') # and also, new footnotes self.footnotestack.append(self.collect_footnotes(node)) - self.curfilestack.append(node['file']) + # also add a document target + self.body.append('\\hypertarget{--doc-%s}{}' % node['docname']) + self.curfilestack.append(node['docname']) def collect_footnotes(self, node): fnotes = {} @@ -308,7 +317,8 @@ class LaTeXTranslator(nodes.NodeVisitor): # self.body.append(r'\hypertarget{%s}{}' % id) # self.written_ids.add(id) def depart_section(self, node): - self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1) + self.sectionlevel = max(self.sectionlevel - 1, + self.top_sectionlevel - 1) def visit_problematic(self, node): self.body.append(r'{\color{red}\bfseries{}') @@ -339,7 +349,8 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_production(self, node): if node['tokenname']: - self.body.append('\\production{%s}{' % self.encode(node['tokenname'])) + self.body.append('\\production{%s}{' % + self.encode(node['tokenname'])) else: self.body.append('\\productioncont{') def depart_production(self, node): @@ -356,8 +367,12 @@ class LaTeXTranslator(nodes.NodeVisitor): # the environment already handles this raise nodes.SkipNode elif self.this_is_the_title: - if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text): - self.builder.warn('document title is not a single Text node') + if len(node.children) != 1 and not isinstance(node.children[0], + nodes.Text): + self.builder.warn( + 'document title is not a single Text node', + '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]), + node.line or '')) if not self.elements['title']: # text needs to be escaped since it is inserted into # the output literally @@ -383,8 +398,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.caption = self.encode(node.astext()) raise nodes.SkipNode else: - self.builder.warn('encountered title node not in section, topic, ' - 'table, admonition or sidebar') + self.builder.warn( + 'encountered title node not in section, topic, table, ' + 'admonition or sidebar', + '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]), + node.line or '')) self.body.append('\\textbf{') self.context.append('}\n') self.in_title = 1 @@ -405,6 +423,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'function' : 'funcdesc', 'class': 'classdesc', 'method': 'methoddesc', + 'classmethod': 'classmethoddesc', 'staticmethod': 'staticmethoddesc', 'exception': 'excdesc', 'data': 'datadesc', @@ -447,7 +466,7 @@ class LaTeXTranslator(nodes.NodeVisitor): t2 = "{%s}{%s}" % (d.name, d.params) elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'): t2 = "{%s}" % (d.name) - elif d.env in ('methoddesc', 'staticmethoddesc'): + elif d.env in ('methoddesc', 'classmethoddesc', 'staticmethoddesc'): if d.cls: t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params) else: @@ -488,6 +507,14 @@ class LaTeXTranslator(nodes.NodeVisitor): self.descstack[-1].type = self.encode(node.astext().strip()) raise nodes.SkipNode + def visit_desc_returns(self, node): + d = self.descstack[-1] + if d.env == 'describe': + d.name += ' $\\rightarrow$ ' + self.encode(node.astext()) + else: + self.descstack[-1].returns = self.encode(node.astext().strip()) + raise nodes.SkipNode + def visit_desc_name(self, node): d = self.descstack[-1] if d.env == 'describe': @@ -564,8 +591,9 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_table(self, node): if self.table: - raise UnsupportedError('%s:%s: nested tables are not yet implemented.' % - (self.curfilestack[-1], node.line or '')) + raise UnsupportedError( + '%s:%s: nested tables are not yet implemented.' % + (self.curfilestack[-1], node.line or '')) self.table = Table() self.tablebody = [] # Redirect body output until table is finished. @@ -585,7 +613,8 @@ class LaTeXTranslator(nodes.NodeVisitor): else: if self.table.has_verbatim: colwidth = 0.95 / self.table.colcount - colspec = ('p{%.3f\\textwidth}|' % colwidth) * self.table.colcount + colspec = ('p{%.3f\\textwidth}|' % colwidth) * \ + self.table.colcount self.body.append('{|' + colspec + '}\n') else: self.body.append('{|' + ('L|' * self.table.colcount) + '}\n') @@ -649,14 +678,17 @@ class LaTeXTranslator(nodes.NodeVisitor): # this is a list in the source, but should be rendered as a # comma-separated list here self.body.append('\n\n') - self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.') + self.body.append(', '.join(n.astext() + for n in node.children[0].children) + '.') self.body.append('\n\n') raise nodes.SkipNode def visit_bullet_list(self, node): - self.body.append('\\begin{itemize}\n' ) + if not self.compact_list: + self.body.append('\\begin{itemize}\n' ) def depart_bullet_list(self, node): - self.body.append('\\end{itemize}\n' ) + if not self.compact_list: + self.body.append('\\end{itemize}\n' ) def visit_enumerated_list(self, node): self.body.append('\\begin{enumerate}\n' ) @@ -725,12 +757,29 @@ class LaTeXTranslator(nodes.NodeVisitor): def depart_centered(self, node): self.body.append('\n\\end{centering}') + def visit_hlist(self, node): + # for now, we don't support a more compact list format + # don't add individual itemize environments, but one for all columns + self.compact_list += 1 + self.body.append('\\begin{itemize}\\setlength{\\itemsep}{0pt}' + '\\setlength{\\parskip}{0pt}\n') + def depart_hlist(self, node): + self.compact_list -= 1 + self.body.append('\\end{itemize}\n') + + def visit_hlistcol(self, node): + pass + def depart_hlistcol(self, node): + pass + def visit_module(self, node): modname = node['modname'] - self.body.append('\n\\hypertarget{module-%s}{}'%(modname.replace(' ',''))) - self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''), - self.encode(modname))) - self.body.append('\n\\modulesynopsis{%s}' % self.encode(node['synopsis'])) + self.body.append('\n\\hypertarget{module-%s}{}' % + (modname.replace(' ',''))) + self.body.append('\n\\declaremodule[%s]{}{%s}' % ( + modname.replace('_', ''), self.encode(modname))) + self.body.append('\n\\modulesynopsis{%s}' % + self.encode(node['synopsis'])) if node.has_key('platform'): self.body.append('\\platform{%s}' % self.encode(node['platform'])) def depart_module(self, node): @@ -810,17 +859,23 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_figure(self, node): - if (not node.attributes.has_key('align') or - node.attributes['align'] == 'center'): - # centering does not add vertical space like center. - align = '\n\\centering' - align_end = '' + if node.has_key('width') and node.get('align', '') in ('left', 'right'): + self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % + (node['align'] == 'right' and 'r' or 'l', + node['width'])) + self.context.append('\\end{wrapfigure}\n') else: - # TODO non vertical space for other alignments. - align = '\\begin{flush%s}' % node.attributes['align'] - align_end = '\\end{flush%s}' % node.attributes['align'] - self.body.append('\\begin{figure}[htbp]%s\n' % align) - self.context.append('%s\\end{figure}\n' % align_end) + if (not node.attributes.has_key('align') or + node.attributes['align'] == 'center'): + # centering does not add vertical space like center. + align = '\n\\centering' + align_end = '' + else: + # TODO non vertical space for other alignments. + align = '\\begin{flush%s}' % node.attributes['align'] + align_end = '\\end{flush%s}' % node.attributes['align'] + self.body.append('\\begin{figure}[htbp]%s\n' % align) + self.context.append('%s\\end{figure}\n' % align_end) def depart_figure(self, node): self.body.append(self.context.pop()) @@ -907,15 +962,18 @@ class LaTeXTranslator(nodes.NodeVisitor): entries = node['entries'] for type, string, tid, _ in entries: if type == 'single': - self.body.append(r'\index{%s}' % scre.sub('!', self.encode(string))) + self.body.append(r'\index{%s}' % + scre.sub('!', self.encode(string))) elif type == 'pair': - parts = tuple(self.encode(x.strip()) for x in string.split(';', 1)) + parts = tuple(self.encode(x.strip()) + for x in string.split(';', 1)) try: self.body.append(r'\indexii{%s}{%s}' % parts) except TypeError: self.builder.warn('invalid pair index entry %r' % string) elif type == 'triple': - parts = tuple(self.encode(x.strip()) for x in string.split(';', 2)) + parts = tuple(self.encode(x.strip()) + for x in string.split(';', 2)) try: self.body.append(r'\indexiii{%s}{%s}{%s}' % parts) except TypeError: @@ -940,6 +998,12 @@ class LaTeXTranslator(nodes.NodeVisitor): elif uri.startswith('#'): self.body.append('\\hyperlink{%s}{' % uri[1:]) self.context.append('}') + elif uri.startswith('%'): + hashindex = uri.find('#') + targetname = (hashindex == -1) and '--doc-' + uri[1:] \ + or uri[hashindex+1:] + self.body.append('\\hyperlink{%s}{' % targetname) + self.context.append('}') elif uri.startswith('@token'): if self.in_production_list: self.body.append('\\token{') @@ -947,11 +1011,19 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\grammartoken{') self.context.append('}') else: - self.builder.warn('unusable reference target found: %s' % uri) + self.builder.warn( + 'unusable reference target found: %s' % uri, + '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]), + node.line or '')) self.context.append('') def depart_reference(self, node): self.body.append(self.context.pop()) + def visit_download_reference(self, node): + pass + def depart_download_reference(self, node): + pass + def visit_pending_xref(self, node): pass def depart_pending_xref(self, node): @@ -974,6 +1046,18 @@ class LaTeXTranslator(nodes.NodeVisitor): def depart_strong(self, node): self.body.append('}') + def visit_abbreviation(self, node): + abbr = node.astext() + self.body.append(r'\textsc{') + # spell out the explanation once + if node.hasattr('explanation') and abbr not in self.handled_abbrs: + self.context.append('} (%s)' % self.encode(node['explanation'])) + self.handled_abbrs.add(abbr) + else: + self.context.append('}') + def depart_abbreviation(self, node): + self.body.append(self.context.pop()) + def visit_title_reference(self, node): self.body.append(r'\emph{') def depart_title_reference(self, node): diff --git a/sphinx/textwriter.py b/sphinx/writers/text.py index 46bf697c..ceca5c8a 100644 --- a/sphinx/textwriter.py +++ b/sphinx/writers/text.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.textwriter - ~~~~~~~~~~~~~~~~~ + sphinx.writers.text + ~~~~~~~~~~~~~~~~~~~ Custom docutils writer for plain text. @@ -195,6 +195,11 @@ class TextTranslator(nodes.NodeVisitor): def depart_desc_type(self, node): pass + def visit_desc_returns(self, node): + self.add_text(' -> ') + def depart_desc_returns(self, node): + pass + def visit_desc_parameterlist(self, node): self.add_text('(') self.first_param = 1 @@ -414,7 +419,8 @@ class TextTranslator(nodes.NodeVisitor): def visit_acks(self, node): self.new_state(0) - self.add_text(', '.join(n.astext() for n in node.children[0].children) + '.') + self.add_text(', '.join(n.astext() for n in node.children[0].children) + + '.') self.end_state() raise nodes.SkipNode @@ -511,6 +517,16 @@ class TextTranslator(nodes.NodeVisitor): def depart_centered(self, node): pass + def visit_hlist(self, node): + pass + def depart_hlist(self, node): + pass + + def visit_hlistcol(self, node): + pass + def depart_hlistcol(self, node): + pass + def visit_admonition(self, node): self.new_state(0) def depart_admonition(self, node): @@ -609,6 +625,11 @@ class TextTranslator(nodes.NodeVisitor): def depart_reference(self, node): pass + def visit_download_reference(self, node): + pass + def depart_download_reference(self, node): + pass + def visit_emphasis(self, node): self.add_text('*') def depart_emphasis(self, node): @@ -624,6 +645,12 @@ class TextTranslator(nodes.NodeVisitor): def depart_strong(self, node): self.add_text('**') + def visit_abbreviation(self, node): + self.add_text('') + def depart_abbreviation(self, node): + if node.hasattr('explanation'): + self.add_text(' (%s)' % node['explanation']) + def visit_title_reference(self, node): self.add_text('*') def depart_title_reference(self, node): diff --git a/tests/coverage.py b/tests/coverage.py new file mode 100755 index 00000000..148e573c --- /dev/null +++ b/tests/coverage.py @@ -0,0 +1,1163 @@ +#!/usr/bin/python +# +# Perforce Defect Tracking Integration Project +# <http://www.ravenbrook.com/project/p4dti/> +# +# COVERAGE.PY -- COVERAGE TESTING +# +# Gareth Rees, Ravenbrook Limited, 2001-12-04 +# Ned Batchelder, 2004-12-12 +# http://nedbatchelder.com/code/modules/coverage.html +# +# +# 1. INTRODUCTION +# +# This module provides coverage testing for Python code. +# +# The intended readership is all Python developers. +# +# This document is not confidential. +# +# See [GDR 2001-12-04a] for the command-line interface, programmatic +# interface and limitations. See [GDR 2001-12-04b] for requirements and +# design. + +r"""Usage: + +coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...] + Execute module, passing the given command-line arguments, collecting + coverage data. With the -p option, write to a temporary file containing + the machine name and process ID. + +coverage.py -e + Erase collected coverage data. + +coverage.py -c + Collect data from multiple coverage files (as created by -p option above) + and store it into a single file representing the union of the coverage. + +coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ... + Report on the statement coverage for the given files. With the -m + option, show line numbers of the statements that weren't executed. + +coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ... + Make annotated copies of the given files, marking statements that + are executed with > and statements that are missed with !. With + the -d option, make the copies in that directory. Without the -d + option, make each copy in the same directory as the original. + +-o dir,dir2,... + Omit reporting or annotating files when their filename path starts with + a directory listed in the omit list. + e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits + +Coverage data is saved in the file .coverage by default. Set the +COVERAGE_FILE environment variable to save it somewhere else.""" + +__version__ = "2.85.20080914" # see detailed history at the end of this file. + +import compiler +import compiler.visitor +import glob +import os +import re +import string +import symbol +import sys +import threading +import token +import types +import zipimport +from socket import gethostname + +# Python version compatibility +try: + strclass = basestring # new to 2.3 +except: + strclass = str + +# 2. IMPLEMENTATION +# +# This uses the "singleton" pattern. +# +# The word "morf" means a module object (from which the source file can +# be deduced by suitable manipulation of the __file__ attribute) or a +# filename. +# +# When we generate a coverage report we have to canonicalize every +# filename in the coverage dictionary just in case it refers to the +# module we are reporting on. It seems a shame to throw away this +# information so the data in the coverage dictionary is transferred to +# the 'cexecuted' dictionary under the canonical filenames. +# +# The coverage dictionary is called "c" and the trace function "t". The +# reason for these short names is that Python looks up variables by name +# at runtime and so execution time depends on the length of variables! +# In the bottleneck of this application it's appropriate to abbreviate +# names to increase speed. + +class StatementFindingAstVisitor(compiler.visitor.ASTVisitor): + """ A visitor for a parsed Abstract Syntax Tree which finds executable + statements. + """ + def __init__(self, statements, excluded, suite_spots): + compiler.visitor.ASTVisitor.__init__(self) + self.statements = statements + self.excluded = excluded + self.suite_spots = suite_spots + self.excluding_suite = 0 + + def doRecursive(self, node): + for n in node.getChildNodes(): + self.dispatch(n) + + visitStmt = visitModule = doRecursive + + def doCode(self, node): + if hasattr(node, 'decorators') and node.decorators: + self.dispatch(node.decorators) + self.recordAndDispatch(node.code) + else: + self.doSuite(node, node.code) + + visitFunction = visitClass = doCode + + def getFirstLine(self, node): + # Find the first line in the tree node. + lineno = node.lineno + for n in node.getChildNodes(): + f = self.getFirstLine(n) + if lineno and f: + lineno = min(lineno, f) + else: + lineno = lineno or f + return lineno + + def getLastLine(self, node): + # Find the first line in the tree node. + lineno = node.lineno + for n in node.getChildNodes(): + lineno = max(lineno, self.getLastLine(n)) + return lineno + + def doStatement(self, node): + self.recordLine(self.getFirstLine(node)) + + visitAssert = visitAssign = visitAssTuple = visitPrint = \ + visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ + doStatement + + def visitPass(self, node): + # Pass statements have weird interactions with docstrings. If this + # pass statement is part of one of those pairs, claim that the statement + # is on the later of the two lines. + l = node.lineno + if l: + lines = self.suite_spots.get(l, [l,l]) + self.statements[lines[1]] = 1 + + def visitDiscard(self, node): + # Discard nodes are statements that execute an expression, but then + # discard the results. This includes function calls, so we can't + # ignore them all. But if the expression is a constant, the statement + # won't be "executed", so don't count it now. + if node.expr.__class__.__name__ != 'Const': + self.doStatement(node) + + def recordNodeLine(self, node): + # Stmt nodes often have None, but shouldn't claim the first line of + # their children (because the first child might be an ignorable line + # like "global a"). + if node.__class__.__name__ != 'Stmt': + return self.recordLine(self.getFirstLine(node)) + else: + return 0 + + def recordLine(self, lineno): + # Returns a bool, whether the line is included or excluded. + if lineno: + # Multi-line tests introducing suites have to get charged to their + # keyword. + if lineno in self.suite_spots: + lineno = self.suite_spots[lineno][0] + # If we're inside an excluded suite, record that this line was + # excluded. + if self.excluding_suite: + self.excluded[lineno] = 1 + return 0 + # If this line is excluded, or suite_spots maps this line to + # another line that is exlcuded, then we're excluded. + elif self.excluded.has_key(lineno) or \ + self.suite_spots.has_key(lineno) and \ + self.excluded.has_key(self.suite_spots[lineno][1]): + return 0 + # Otherwise, this is an executable line. + else: + self.statements[lineno] = 1 + return 1 + return 0 + + default = recordNodeLine + + def recordAndDispatch(self, node): + self.recordNodeLine(node) + self.dispatch(node) + + def doSuite(self, intro, body, exclude=0): + exsuite = self.excluding_suite + if exclude or (intro and not self.recordNodeLine(intro)): + self.excluding_suite = 1 + self.recordAndDispatch(body) + self.excluding_suite = exsuite + + def doPlainWordSuite(self, prevsuite, suite): + # Finding the exclude lines for else's is tricky, because they aren't + # present in the compiler parse tree. Look at the previous suite, + # and find its last line. If any line between there and the else's + # first line are excluded, then we exclude the else. + lastprev = self.getLastLine(prevsuite) + firstelse = self.getFirstLine(suite) + for l in range(lastprev+1, firstelse): + if self.suite_spots.has_key(l): + self.doSuite(None, suite, exclude=self.excluded.has_key(l)) + break + else: + self.doSuite(None, suite) + + def doElse(self, prevsuite, node): + if node.else_: + self.doPlainWordSuite(prevsuite, node.else_) + + def visitFor(self, node): + self.doSuite(node, node.body) + self.doElse(node.body, node) + + visitWhile = visitFor + + def visitIf(self, node): + # The first test has to be handled separately from the rest. + # The first test is credited to the line with the "if", but the others + # are credited to the line with the test for the elif. + self.doSuite(node, node.tests[0][1]) + for t, n in node.tests[1:]: + self.doSuite(t, n) + self.doElse(node.tests[-1][1], node) + + def visitTryExcept(self, node): + self.doSuite(node, node.body) + for i in range(len(node.handlers)): + a, b, h = node.handlers[i] + if not a: + # It's a plain "except:". Find the previous suite. + if i > 0: + prev = node.handlers[i-1][2] + else: + prev = node.body + self.doPlainWordSuite(prev, h) + else: + self.doSuite(a, h) + self.doElse(node.handlers[-1][2], node) + + def visitTryFinally(self, node): + self.doSuite(node, node.body) + self.doPlainWordSuite(node.body, node.final) + + def visitWith(self, node): + self.doSuite(node, node.body) + + def visitGlobal(self, node): + # "global" statements don't execute like others (they don't call the + # trace function), so don't record their line numbers. + pass + +the_coverage = None + +class CoverageException(Exception): + pass + +class coverage: + # Name of the cache file (unless environment variable is set). + cache_default = ".coverage" + + # Environment variable naming the cache file. + cache_env = "COVERAGE_FILE" + + # A dictionary with an entry for (Python source file name, line number + # in that file) if that line has been executed. + c = {} + + # A map from canonical Python source file name to a dictionary in + # which there's an entry for each line number that has been + # executed. + cexecuted = {} + + # Cache of results of calling the analysis2() method, so that you can + # specify both -r and -a without doing double work. + analysis_cache = {} + + # Cache of results of calling the canonical_filename() method, to + # avoid duplicating work. + canonical_filename_cache = {} + + def __init__(self): + global the_coverage + if the_coverage: + raise CoverageException("Only one coverage object allowed.") + self.usecache = 1 + self.cache = None + self.parallel_mode = False + self.exclude_re = '' + self.nesting = 0 + self.cstack = [] + self.xstack = [] + self.relative_dir = self.abs_file(os.curdir)+os.sep + self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]') + + # t(f, x, y). This method is passed to sys.settrace as a trace function. + # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and + # the arguments and return value of the trace function. + # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code + # objects. + + def t(self, f, w, unused): #pragma: no cover + if w == 'line': + self.c[(f.f_code.co_filename, f.f_lineno)] = 1 + #-for c in self.cstack: + #- c[(f.f_code.co_filename, f.f_lineno)] = 1 + return self.t + + def help(self, error=None): #pragma: no cover + if error: + print error + print + print __doc__ + sys.exit(1) + + def command_line(self, argv, help_fn=None): + import getopt + help_fn = help_fn or self.help + settings = {} + optmap = { + '-a': 'annotate', + '-c': 'collect', + '-d:': 'directory=', + '-e': 'erase', + '-h': 'help', + '-i': 'ignore-errors', + '-m': 'show-missing', + '-p': 'parallel-mode', + '-r': 'report', + '-x': 'execute', + '-o:': 'omit=', + } + short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '') + long_opts = optmap.values() + options, args = getopt.getopt(argv, short_opts, long_opts) + for o, a in options: + if optmap.has_key(o): + settings[optmap[o]] = 1 + elif optmap.has_key(o + ':'): + settings[optmap[o + ':']] = a + elif o[2:] in long_opts: + settings[o[2:]] = 1 + elif o[2:] + '=' in long_opts: + settings[o[2:]+'='] = a + else: #pragma: no cover + pass # Can't get here, because getopt won't return anything unknown. + + if settings.get('help'): + help_fn() + + for i in ['erase', 'execute']: + for j in ['annotate', 'report', 'collect']: + if settings.get(i) and settings.get(j): + help_fn("You can't specify the '%s' and '%s' " + "options at the same time." % (i, j)) + + args_needed = (settings.get('execute') + or settings.get('annotate') + or settings.get('report')) + action = (settings.get('erase') + or settings.get('collect') + or args_needed) + if not action: + help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") + if not args_needed and args: + help_fn("Unexpected arguments: %s" % " ".join(args)) + + self.parallel_mode = settings.get('parallel-mode') + self.get_ready() + + if settings.get('erase'): + self.erase() + if settings.get('execute'): + if not args: + help_fn("Nothing to do.") + sys.argv = args + self.start() + import __main__ + sys.path[0] = os.path.dirname(sys.argv[0]) + execfile(sys.argv[0], __main__.__dict__) + if settings.get('collect'): + self.collect() + if not args: + args = self.cexecuted.keys() + + ignore_errors = settings.get('ignore-errors') + show_missing = settings.get('show-missing') + directory = settings.get('directory=') + + omit = settings.get('omit=') + if omit is not None: + omit = [self.abs_file(p) for p in omit.split(',')] + else: + omit = [] + + if settings.get('report'): + self.report(args, show_missing, ignore_errors, omit_prefixes=omit) + if settings.get('annotate'): + self.annotate(args, directory, ignore_errors, omit_prefixes=omit) + + def use_cache(self, usecache, cache_file=None): + self.usecache = usecache + if cache_file and not self.cache: + self.cache_default = cache_file + + def get_ready(self, parallel_mode=False): + if self.usecache and not self.cache: + self.cache = os.environ.get(self.cache_env, self.cache_default) + if self.parallel_mode: + self.cache += "." + gethostname() + "." + str(os.getpid()) + self.restore() + self.analysis_cache = {} + + def start(self, parallel_mode=False): + self.get_ready() + if self.nesting == 0: #pragma: no cover + sys.settrace(self.t) + if hasattr(threading, 'settrace'): + threading.settrace(self.t) + self.nesting += 1 + + def stop(self): + self.nesting -= 1 + if self.nesting == 0: #pragma: no cover + sys.settrace(None) + if hasattr(threading, 'settrace'): + threading.settrace(None) + + def erase(self): + self.get_ready() + self.c = {} + self.analysis_cache = {} + self.cexecuted = {} + if self.cache and os.path.exists(self.cache): + os.remove(self.cache) + + def exclude(self, re): + if self.exclude_re: + self.exclude_re += "|" + self.exclude_re += "(" + re + ")" + + def begin_recursive(self): + self.cstack.append(self.c) + self.xstack.append(self.exclude_re) + + def end_recursive(self): + self.c = self.cstack.pop() + self.exclude_re = self.xstack.pop() + + # save(). Save coverage data to the coverage cache. + + def save(self): + if self.usecache and self.cache: + self.canonicalize_filenames() + cache = open(self.cache, 'wb') + import marshal + marshal.dump(self.cexecuted, cache) + cache.close() + + # restore(). Restore coverage data from the coverage cache (if it exists). + + def restore(self): + self.c = {} + self.cexecuted = {} + assert self.usecache + if os.path.exists(self.cache): + self.cexecuted = self.restore_file(self.cache) + + def restore_file(self, file_name): + try: + cache = open(file_name, 'rb') + import marshal + cexecuted = marshal.load(cache) + cache.close() + if isinstance(cexecuted, types.DictType): + return cexecuted + else: + return {} + except: + return {} + + # collect(). Collect data in multiple files produced by parallel mode + + def collect(self): + cache_dir, local = os.path.split(self.cache) + for f in os.listdir(cache_dir or '.'): + if not f.startswith(local): + continue + + full_path = os.path.join(cache_dir, f) + cexecuted = self.restore_file(full_path) + self.merge_data(cexecuted) + + def merge_data(self, new_data): + for file_name, file_data in new_data.items(): + if self.cexecuted.has_key(file_name): + self.merge_file_data(self.cexecuted[file_name], file_data) + else: + self.cexecuted[file_name] = file_data + + def merge_file_data(self, cache_data, new_data): + for line_number in new_data.keys(): + if not cache_data.has_key(line_number): + cache_data[line_number] = new_data[line_number] + + def abs_file(self, filename): + """ Helper function to turn a filename into an absolute normalized + filename. + """ + return os.path.normcase(os.path.abspath(os.path.realpath(filename))) + + def get_zip_data(self, filename): + """ Get data from `filename` if it is a zip file path, or return None + if it is not. + """ + markers = ['.zip'+os.sep, '.egg'+os.sep] + for marker in markers: + if marker in filename: + parts = filename.split(marker) + try: + zi = zipimport.zipimporter(parts[0]+marker[:-1]) + except zipimport.ZipImportError: + continue + try: + data = zi.get_data(parts[1]) + except IOError: + continue + return data + return None + + # canonical_filename(filename). Return a canonical filename for the + # file (that is, an absolute path with no redundant components and + # normalized case). See [GDR 2001-12-04b, 3.3]. + + def canonical_filename(self, filename): + if not self.canonical_filename_cache.has_key(filename): + f = filename + if os.path.isabs(f) and not os.path.exists(f): + if not self.get_zip_data(f): + f = os.path.basename(f) + if not os.path.isabs(f): + for path in [os.curdir] + sys.path: + g = os.path.join(path, f) + if os.path.exists(g): + f = g + break + cf = self.abs_file(f) + self.canonical_filename_cache[filename] = cf + return self.canonical_filename_cache[filename] + + # canonicalize_filenames(). Copy results from "c" to "cexecuted", + # canonicalizing filenames on the way. Clear the "c" map. + + def canonicalize_filenames(self): + for filename, lineno in self.c.keys(): + if filename == '<string>': + # Can't do anything useful with exec'd strings, so skip them. + continue + f = self.canonical_filename(filename) + if not self.cexecuted.has_key(f): + self.cexecuted[f] = {} + self.cexecuted[f][lineno] = 1 + self.c = {} + + # morf_filename(morf). Return the filename for a module or file. + + def morf_filename(self, morf): + if hasattr(morf, '__file__'): + f = morf.__file__ + else: + f = morf + return self.canonical_filename(f) + + # analyze_morf(morf). Analyze the module or filename passed as + # the argument. If the source code can't be found, raise an error. + # Otherwise, return a tuple of (1) the canonical filename of the + # source code for the module, (2) a list of lines of statements + # in the source code, (3) a list of lines of excluded statements, + # and (4), a map of line numbers to multi-line line number ranges, for + # statements that cross lines. + + def analyze_morf(self, morf): + if self.analysis_cache.has_key(morf): + return self.analysis_cache[morf] + filename = self.morf_filename(morf) + ext = os.path.splitext(filename)[1] + source, sourcef = None, None + if ext == '.pyc': + if not os.path.exists(filename[:-1]): + source = self.get_zip_data(filename[:-1]) + if not source: + raise CoverageException( + "No source for compiled code '%s'." % filename + ) + filename = filename[:-1] + if not source: + sourcef = open(filename, 'rU') + source = sourcef.read() + try: + lines, excluded_lines, line_map = self.find_executable_statements( + source, exclude=self.exclude_re + ) + except SyntaxError, synerr: + raise CoverageException( + "Couldn't parse '%s' as Python source: '%s' at line %d" % + (filename, synerr.msg, synerr.lineno) + ) + if sourcef: + sourcef.close() + result = filename, lines, excluded_lines, line_map + self.analysis_cache[morf] = result + return result + + def first_line_of_tree(self, tree): + while True: + if len(tree) == 3 and type(tree[2]) == type(1): + return tree[2] + tree = tree[1] + + def last_line_of_tree(self, tree): + while True: + if len(tree) == 3 and type(tree[2]) == type(1): + return tree[2] + tree = tree[-1] + + def find_docstring_pass_pair(self, tree, spots): + for i in range(1, len(tree)): + if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): + first_line = self.first_line_of_tree(tree[i]) + last_line = self.last_line_of_tree(tree[i+1]) + self.record_multiline(spots, first_line, last_line) + + def is_string_constant(self, tree): + try: + return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt + except: + return False + + def is_pass_stmt(self, tree): + try: + return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt + except: + return False + + def record_multiline(self, spots, i, j): + for l in range(i, j+1): + spots[l] = (i, j) + + def get_suite_spots(self, tree, spots): + """ Analyze a parse tree to find suite introducers which span a number + of lines. + """ + for i in range(1, len(tree)): + if type(tree[i]) == type(()): + if tree[i][0] == symbol.suite: + # Found a suite, look back for the colon and keyword. + lineno_colon = lineno_word = None + for j in range(i-1, 0, -1): + if tree[j][0] == token.COLON: + # Colons are never executed themselves: we want the + # line number of the last token before the colon. + lineno_colon = self.last_line_of_tree(tree[j-1]) + elif tree[j][0] == token.NAME: + if tree[j][1] == 'elif': + # Find the line number of the first non-terminal + # after the keyword. + t = tree[j+1] + while t and token.ISNONTERMINAL(t[0]): + t = t[1] + if t: + lineno_word = t[2] + else: + lineno_word = tree[j][2] + break + elif tree[j][0] == symbol.except_clause: + # "except" clauses look like: + # ('except_clause', ('NAME', 'except', lineno), ...) + if tree[j][1][0] == token.NAME: + lineno_word = tree[j][1][2] + break + if lineno_colon and lineno_word: + # Found colon and keyword, mark all the lines + # between the two with the two line numbers. + self.record_multiline(spots, lineno_word, lineno_colon) + + # "pass" statements are tricky: different versions of Python + # treat them differently, especially in the common case of a + # function with a doc string and a single pass statement. + self.find_docstring_pass_pair(tree[i], spots) + + elif tree[i][0] == symbol.simple_stmt: + first_line = self.first_line_of_tree(tree[i]) + last_line = self.last_line_of_tree(tree[i]) + if first_line != last_line: + self.record_multiline(spots, first_line, last_line) + self.get_suite_spots(tree[i], spots) + + def find_executable_statements(self, text, exclude=None): + # Find lines which match an exclusion pattern. + excluded = {} + suite_spots = {} + if exclude: + reExclude = re.compile(exclude) + lines = text.split('\n') + for i in range(len(lines)): + if reExclude.search(lines[i]): + excluded[i+1] = 1 + + # Parse the code and analyze the parse tree to find out which statements + # are multiline, and where suites begin and end. + import parser + tree = parser.suite(text+'\n\n').totuple(1) + self.get_suite_spots(tree, suite_spots) + #print "Suite spots:", suite_spots + + # Use the compiler module to parse the text and find the executable + # statements. We add newlines to be impervious to final partial lines. + statements = {} + ast = compiler.parse(text+'\n\n') + visitor = StatementFindingAstVisitor(statements, excluded, suite_spots) + compiler.walk(ast, visitor, walker=visitor) + + lines = statements.keys() + lines.sort() + excluded_lines = excluded.keys() + excluded_lines.sort() + return lines, excluded_lines, suite_spots + + # format_lines(statements, lines). Format a list of line numbers + # for printing by coalescing groups of lines as long as the lines + # represent consecutive statements. This will coalesce even if + # there are gaps between statements, so if statements = + # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then + # format_lines will return "1-2, 5-11, 13-14". + + def format_lines(self, statements, lines): + pairs = [] + i = 0 + j = 0 + start = None + pairs = [] + while i < len(statements) and j < len(lines): + if statements[i] == lines[j]: + if start == None: + start = lines[j] + end = lines[j] + j = j + 1 + elif start: + pairs.append((start, end)) + start = None + i = i + 1 + if start: + pairs.append((start, end)) + def stringify(pair): + start, end = pair + if start == end: + return "%d" % start + else: + return "%d-%d" % (start, end) + ret = string.join(map(stringify, pairs), ", ") + return ret + + # Backward compatibility with version 1. + def analysis(self, morf): + f, s, _, m, mf = self.analysis2(morf) + return f, s, m, mf + + def analysis2(self, morf): + filename, statements, excluded, line_map = self.analyze_morf(morf) + self.canonicalize_filenames() + if not self.cexecuted.has_key(filename): + self.cexecuted[filename] = {} + missing = [] + for line in statements: + lines = line_map.get(line, [line, line]) + for l in range(lines[0], lines[1]+1): + if self.cexecuted[filename].has_key(l): + break + else: + missing.append(line) + return (filename, statements, excluded, missing, + self.format_lines(statements, missing)) + + def relative_filename(self, filename): + """ Convert filename to relative filename from self.relative_dir. + """ + return filename.replace(self.relative_dir, "") + + def morf_name(self, morf): + """ Return the name of morf as used in report. + """ + if hasattr(morf, '__name__'): + return morf.__name__ + else: + return self.relative_filename(os.path.splitext(morf)[0]) + + def filter_by_prefix(self, morfs, omit_prefixes): + """ Return list of morfs where the morf name does not begin + with any one of the omit_prefixes. + """ + filtered_morfs = [] + for morf in morfs: + for prefix in omit_prefixes: + if self.morf_name(morf).startswith(prefix): + break + else: + filtered_morfs.append(morf) + + return filtered_morfs + + def morf_name_compare(self, x, y): + return cmp(self.morf_name(x), self.morf_name(y)) + + def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]): + if not isinstance(morfs, types.ListType): + morfs = [morfs] + # On windows, the shell doesn't expand wildcards. Do it here. + globbed = [] + for morf in morfs: + if isinstance(morf, strclass): + globbed.extend(glob.glob(morf)) + else: + globbed.append(morf) + morfs = globbed + + morfs = self.filter_by_prefix(morfs, omit_prefixes) + morfs.sort(self.morf_name_compare) + + max_name = max([5,] + map(len, map(self.morf_name, morfs))) + fmt_name = "%%- %ds " % max_name + fmt_err = fmt_name + "%s: %s" + header = fmt_name % "Name" + " Stmts Exec Cover" + fmt_coverage = fmt_name + "% 6d % 6d % 5d%%" + if show_missing: + header = header + " Missing" + fmt_coverage = fmt_coverage + " %s" + if not file: + file = sys.stdout + print >>file, header + print >>file, "-" * len(header) + total_statements = 0 + total_executed = 0 + for morf in morfs: + name = self.morf_name(morf) + try: + _, statements, _, missing, readable = self.analysis2(morf) + n = len(statements) + m = n - len(missing) + if n > 0: + pc = 100.0 * m / n + else: + pc = 100.0 + args = (name, n, m, pc) + if show_missing: + args = args + (readable,) + print >>file, fmt_coverage % args + total_statements = total_statements + n + total_executed = total_executed + m + except KeyboardInterrupt: #pragma: no cover + raise + except: + if not ignore_errors: + typ, msg = sys.exc_info()[:2] + print >>file, fmt_err % (name, typ, msg) + if len(morfs) > 1: + print >>file, "-" * len(header) + if total_statements > 0: + pc = 100.0 * total_executed / total_statements + else: + pc = 100.0 + args = ("TOTAL", total_statements, total_executed, pc) + if show_missing: + args = args + ("",) + print >>file, fmt_coverage % args + + # annotate(morfs, ignore_errors). + + blank_re = re.compile(r"\s*(#|$)") + else_re = re.compile(r"\s*else\s*:\s*(#|$)") + + def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]): + morfs = self.filter_by_prefix(morfs, omit_prefixes) + for morf in morfs: + try: + filename, statements, excluded, missing, _ = self.analysis2(morf) + self.annotate_file(filename, statements, excluded, missing, directory) + except KeyboardInterrupt: + raise + except: + if not ignore_errors: + raise + + def annotate_file(self, filename, statements, excluded, missing, directory=None): + source = open(filename, 'r') + if directory: + dest_file = os.path.join(directory, + os.path.basename(filename) + + ',cover') + else: + dest_file = filename + ',cover' + dest = open(dest_file, 'w') + lineno = 0 + i = 0 + j = 0 + covered = 1 + while 1: + line = source.readline() + if line == '': + break + lineno = lineno + 1 + while i < len(statements) and statements[i] < lineno: + i = i + 1 + while j < len(missing) and missing[j] < lineno: + j = j + 1 + if i < len(statements) and statements[i] == lineno: + covered = j >= len(missing) or missing[j] > lineno + if self.blank_re.match(line): + dest.write(' ') + elif self.else_re.match(line): + # Special logic for lines containing only 'else:'. + # See [GDR 2001-12-04b, 3.2]. + if i >= len(statements) and j >= len(missing): + dest.write('! ') + elif i >= len(statements) or j >= len(missing): + dest.write('> ') + elif statements[i] == missing[j]: + dest.write('! ') + else: + dest.write('> ') + elif lineno in excluded: + dest.write('- ') + elif covered: + dest.write('> ') + else: + dest.write('! ') + dest.write(line) + source.close() + dest.close() + +# Singleton object. +the_coverage = coverage() + +# Module functions call methods in the singleton object. +def use_cache(*args, **kw): + return the_coverage.use_cache(*args, **kw) + +def start(*args, **kw): + return the_coverage.start(*args, **kw) + +def stop(*args, **kw): + return the_coverage.stop(*args, **kw) + +def erase(*args, **kw): + return the_coverage.erase(*args, **kw) + +def begin_recursive(*args, **kw): + return the_coverage.begin_recursive(*args, **kw) + +def end_recursive(*args, **kw): + return the_coverage.end_recursive(*args, **kw) + +def exclude(*args, **kw): + return the_coverage.exclude(*args, **kw) + +def analysis(*args, **kw): + return the_coverage.analysis(*args, **kw) + +def analysis2(*args, **kw): + return the_coverage.analysis2(*args, **kw) + +def report(*args, **kw): + return the_coverage.report(*args, **kw) + +def annotate(*args, **kw): + return the_coverage.annotate(*args, **kw) + +def annotate_file(*args, **kw): + return the_coverage.annotate_file(*args, **kw) + +# Save coverage data when Python exits. (The atexit module wasn't +# introduced until Python 2.0, so use sys.exitfunc when it's not +# available.) +try: + import atexit + atexit.register(the_coverage.save) +except ImportError: + sys.exitfunc = the_coverage.save + +def main(): + the_coverage.command_line(sys.argv[1:]) + +# Command-line interface. +if __name__ == '__main__': + main() + + +# A. REFERENCES +# +# [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees; +# Ravenbrook Limited; 2001-12-04; +# <http://www.nedbatchelder.com/code/modules/rees-coverage.html>. +# +# [GDR 2001-12-04b] "Statement coverage for Python: design and +# analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04; +# <http://www.nedbatchelder.com/code/modules/rees-design.html>. +# +# [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)"; +# Guide van Rossum; 2001-07-20; +# <http://www.python.org/doc/2.1.1/ref/ref.html>. +# +# [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum; +# 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>. +# +# +# B. DOCUMENT HISTORY +# +# 2001-12-04 GDR Created. +# +# 2001-12-06 GDR Added command-line interface and source code +# annotation. +# +# 2001-12-09 GDR Moved design and interface to separate documents. +# +# 2001-12-10 GDR Open cache file as binary on Windows. Allow +# simultaneous -e and -x, or -a and -r. +# +# 2001-12-12 GDR Added command-line help. Cache analysis so that it +# only needs to be done once when you specify -a and -r. +# +# 2001-12-13 GDR Improved speed while recording. Portable between +# Python 1.5.2 and 2.1.1. +# +# 2002-01-03 GDR Module-level functions work correctly. +# +# 2002-01-07 GDR Update sys.path when running a file with the -x option, +# so that it matches the value the program would get if it were run on +# its own. +# +# 2004-12-12 NMB Significant code changes. +# - Finding executable statements has been rewritten so that docstrings and +# other quirks of Python execution aren't mistakenly identified as missing +# lines. +# - Lines can be excluded from consideration, even entire suites of lines. +# - The filesystem cache of covered lines can be disabled programmatically. +# - Modernized the code. +# +# 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior +# and add 'analysis2'. Add a global for 'annotate', and factor it, adding +# 'annotate_file'. +# +# 2004-12-31 NMB Allow for keyword arguments in the module global functions. +# Thanks, Allen. +# +# 2005-12-02 NMB Call threading.settrace so that all threads are measured. +# Thanks Martin Fuzzey. Add a file argument to report so that reports can be +# captured to a different destination. +# +# 2005-12-03 NMB coverage.py can now measure itself. +# +# 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames, +# and sorting and omitting files to report on. +# +# 2006-07-23 NMB Applied Joseph Tate's patch for function decorators. +# +# 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument +# handling. +# +# 2006-08-22 NMB Applied Geoff Bache's parallel mode patch. +# +# 2006-08-23 NMB Refactorings to improve testability. Fixes to command-line +# logic for parallel mode and collect. +# +# 2006-08-25 NMB "#pragma: nocover" is excluded by default. +# +# 2006-09-10 NMB Properly ignore docstrings and other constant expressions that +# appear in the middle of a function, a problem reported by Tim Leslie. +# Minor changes to avoid lint warnings. +# +# 2006-09-17 NMB coverage.erase() shouldn't clobber the exclude regex. +# Change how parallel mode is invoked, and fix erase() so that it erases the +# cache when called programmatically. +# +# 2007-07-21 NMB In reports, ignore code executed from strings, since we can't +# do anything useful with it anyway. +# Better file handling on Linux, thanks Guillaume Chazarain. +# Better shell support on Windows, thanks Noel O'Boyle. +# Python 2.2 support maintained, thanks Catherine Proulx. +# +# 2007-07-22 NMB Python 2.5 now fully supported. The method of dealing with +# multi-line statements is now less sensitive to the exact line that Python +# reports during execution. Pass statements are handled specially so that their +# disappearance during execution won't throw off the measurement. +# +# 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the +# new with statement is counted as executable. +# +# 2007-07-29 NMB Better packaging. +# +# 2007-09-30 NMB Don't try to predict whether a file is Python source based on +# the extension. Extensionless files are often Pythons scripts. Instead, simply +# parse the file and catch the syntax errors. Hat tip to Ben Finney. +# +# 2008-05-25 NMB Open files in rU mode to avoid line ending craziness. +# Thanks, Edward Loper. +# +# 2008-09-14 NMB Add support for finding source files in eggs. +# Don't check for morf's being instances of ModuleType, instead use duck typing +# so that pseudo-modules can participate. Thanks, Imri Goldberg. +# Use os.realpath as part of the fixing of filenames so that symlinks won't +# confuse things. Thanks, Patrick Mezard. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright 2001 Gareth Rees. All rights reserved. +# Copyright 2004-2008 Ned Batchelder. 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 +# HOLDERS AND 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. +# +# $Id: coverage.py 100 2008-10-12 12:08:22Z nedbat $ diff --git a/tests/root/_templates/layout.html b/tests/root/_templates/layout.html index 1f4688e6..e8920025 100644 --- a/tests/root/_templates/layout.html +++ b/tests/root/_templates/layout.html @@ -1,4 +1,5 @@ {% extends "!layout.html" %} {% block extrahead %} <meta name="hc" content="{{ hckey }}" /> +{{ super() }} {% endblock %} diff --git a/tests/root/autodoc.txt b/tests/root/autodoc.txt index 2b57e0e9..c718feb4 100644 --- a/tests/root/autodoc.txt +++ b/tests/root/autodoc.txt @@ -1,7 +1,28 @@ Autodoc tests ============= +Just testing a few autodoc possibilities... + +.. automodule:: util + .. automodule:: test_autodoc :members: .. autofunction:: function + +.. autoclass:: Class + :inherited-members: + + Additional content. + +.. autoclass:: Outer + :members: Inner + +.. autoattribute:: Class.docattr + +.. autoexception:: CustomEx + :members: f + +.. autoclass:: CustomDict + :show-inheritance: + :members: diff --git a/tests/root/autosummary.txt b/tests/root/autosummary.txt new file mode 100644 index 00000000..edf75e32 --- /dev/null +++ b/tests/root/autosummary.txt @@ -0,0 +1,7 @@ +Autosummary test +================ + +.. autosummary:: + :toctree: generated + + sphinx.application.TemplateBridge diff --git a/tests/root/conf.py b/tests/root/conf.py index 12951d03..fd82be7d 100644 --- a/tests/root/conf.py +++ b/tests/root/conf.py @@ -1,183 +1,71 @@ # -*- coding: utf-8 -*- -# -# Sphinx Tests documentation build configuration file, created by -# sphinx-quickstart on Wed Jun 4 23:49:58 2008. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). -# -# All configuration values have a default value; values that are commented out -# serve to show the default value. import sys, os -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. sys.path.append(os.path.abspath('.')) -# General configuration -# --------------------- - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['ext', 'sphinx.ext.autodoc', 'sphinx.ext.jsmath', - 'sphinx.ext.coverage', 'sphinx.ext.todo'] + 'sphinx.ext.coverage', 'sphinx.ext.todo', + 'sphinx.ext.autosummary'] + jsmath_path = 'dummy.js' -# Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -# The suffix of source filenames. -source_suffix = '.txt' - -# The master toctree document. master_doc = 'contents' +source_suffix = '.txt' -# General substitutions. project = 'Sphinx <Tests>' copyright = '2008, Georg Brandl & Team' - -# The default replacements for |version| and |release|, also used in various -# other places throughout the built documents. -# -# The short X.Y version. -version = '0.4' -# The full version, including alpha/beta/rc tags. -release = '0.4alpha1' - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. +# If this is changed, remember to update the versionchanges! +version = '0.6' +release = '0.6alpha1' today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. #unused_docs = [] - -# List of directories, relative to source directories, that shouldn't be searched -# for source files. exclude_trees = ['_build'] - keep_warnings = True - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' +rst_epilog = '.. |subst| replace:: global substitution' -# Options for HTML output -# ----------------------- +html_theme = 'testtheme' +html_theme_path = ['.'] +html_theme_options = {'testopt': 'testoverride'} -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. html_style = 'default.css' - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (within the static path) to place at the top of -# the sidebar. -#html_logo = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the reST sources are included in the HTML build as _sources/<name>. -#html_copy_source = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - html_context = {'hckey': 'hcval'} -# Output file base name for HTML help builder. htmlhelp_basename = 'SphinxTestsdoc' - -# Options for LaTeX output -# ------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation', 'Georg Brandl', 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True +latex_additional_files = ['svgimg.svg'] value_from_conf_py = 84 coverage_c_path = ['special/*.h'] coverage_c_regexes = {'cfunction': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'} +autosummary_generate = ['autosummary'] + +# modify tags from conf.py +tags.add('confpytag') + +from sphinx import addnodes + +def userdesc_parse(env, sig, signode): + x, y = sig.split(':') + signode += addnodes.desc_name(x, x) + signode += addnodes.desc_parameterlist() + signode[-1] += addnodes.desc_parameter(y, y) + return x def setup(app): app.add_config_value('value_from_conf_py', 42, False) + app.add_description_unit('userdesc', 'userdescrole', '%s (userdesc)', + userdesc_parse) diff --git a/tests/root/contents.txt b/tests/root/contents.txt index 61646f62..24d790a5 100644 --- a/tests/root/contents.txt +++ b/tests/root/contents.txt @@ -9,14 +9,19 @@ Contents: .. toctree:: :maxdepth: 2 + :numbered: images subdir/images + subdir/includes includes markup desc math autodoc + autosummary + + Python <http://python.org/> Indices and tables ================== diff --git a/tests/root/desc.txt b/tests/root/desc.txt index beb470b6..d6915dc2 100644 --- a/tests/root/desc.txt +++ b/tests/root/desc.txt @@ -58,3 +58,14 @@ Testing references ================== Referencing :class:`mod.Cls` or :Class:`mod.Cls` should be the same. + + +User markup +=========== + +.. userdesc:: myobj:parameter + + Description of userdesc. + + +Referencing :userdescrole:`myobj`. diff --git a/tests/root/images.txt b/tests/root/images.txt index be868dfe..bd64d573 100644 --- a/tests/root/images.txt +++ b/tests/root/images.txt @@ -23,3 +23,6 @@ Sphinx image handling .. an image with subdir and unspecified extension .. image:: subdir/simg.* + +.. an SVG image (for HTML at least) +.. image:: svgimg.* diff --git a/tests/root/includes.txt b/tests/root/includes.txt index ad507fc6..44e33af0 100644 --- a/tests/root/includes.txt +++ b/tests/root/includes.txt @@ -14,3 +14,33 @@ Test file and literal inclusion :encoding: latin-1 .. include:: wrongenc.inc :encoding: latin-1 + +Literalinclude options +====================== + +.. highlight:: text + +.. cssclass:: inc-pyobj1 +.. literalinclude:: literal.inc + :pyobject: Foo + +.. cssclass:: inc-pyobj2 +.. literalinclude:: literal.inc + :pyobject: Bar.baz + +.. cssclass:: inc-lines +.. literalinclude:: literal.inc + :lines: 6-7,9 + +.. cssclass:: inc-startend +.. literalinclude:: literal.inc + :start-after: coding: utf-8 + :end-before: class Foo + + +Testing downloadable files +========================== + +Download :download:`img.png` here. +Download :download:`this <subdir/img.png>` there. +Don't download :download:`this <nonexisting.png>`. diff --git a/tests/root/literal.inc b/tests/root/literal.inc index a4ce93d2..d5b9890c 100644 --- a/tests/root/literal.inc +++ b/tests/root/literal.inc @@ -2,3 +2,12 @@ # -*- coding: utf-8 -*- foo = u"Including Unicode characters: üöä" + +class Foo: + pass + +class Bar: + def baz(): + pass + +def bar(): pass diff --git a/tests/root/markup.txt b/tests/root/markup.txt index 454762e3..52d407e2 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -11,6 +11,8 @@ Testing various markup :author: Me :keywords: docs, sphinx +A |subst|. + .. _label: :: @@ -82,13 +84,13 @@ Tables Version markup -------------- -.. versionadded:: 0.5 +.. versionadded:: 0.6 Some funny **stuff**. -.. versionchanged:: 0.5 +.. versionchanged:: 0.6 Even more funny stuff. -.. deprecated:: 0.4 +.. deprecated:: 0.6 Boring stuff. @@ -104,6 +106,16 @@ Reference lookup: [Ref1]_ (defined in another file). `Google <http://www.google.com>`_ For everything. +.. hlist:: + :columns: 4 + + * This + * is + * a horizontal + * list + * with several + * items + .. rubric:: Side note This is a side note. @@ -140,6 +152,7 @@ Option list: try2_stmt: "try" ":" `suite` : "finally" ":" `suite` +Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`. Index markup ------------ @@ -164,6 +177,26 @@ Invalid index markup... Testing öäü... +Only directive +-------------- + +.. only:: html + + In HTML. + +.. only:: latex + + In LaTeX. + +.. only:: html or latex + + In both. + +.. only:: confpytag and (testtag or nonexisting_tag) + + Always present, because set through conf.py/command line. + + .. rubric:: Footnotes .. [#] Like footnotes. diff --git a/tests/root/rimg.png b/tests/root/rimg.png Binary files differnew file mode 100644 index 00000000..1081dc14 --- /dev/null +++ b/tests/root/rimg.png diff --git a/tests/root/special/code.py b/tests/root/special/code.py new file mode 100644 index 00000000..70c48d2e --- /dev/null +++ b/tests/root/special/code.py @@ -0,0 +1,2 @@ +print "line 1" +print "line 2" diff --git a/tests/root/subdir/images.txt b/tests/root/subdir/images.txt index 33adf5b5..f2adf88d 100644 --- a/tests/root/subdir/images.txt +++ b/tests/root/subdir/images.txt @@ -2,3 +2,5 @@ Image including source in subdir ================================ .. image:: img.* + +.. image:: /rimg.png diff --git a/tests/root/subdir/includes.txt b/tests/root/subdir/includes.txt new file mode 100644 index 00000000..3e1ae0d1 --- /dev/null +++ b/tests/root/subdir/includes.txt @@ -0,0 +1,12 @@ +Including in subdir +=================== + +.. absolute filename +.. literalinclude:: /special/code.py + :lines: 1 + +.. relative filename +.. literalinclude:: ../special/code.py + :lines: 2 + +Absolute :download:`/img.png` download. diff --git a/tests/root/svgimg.pdf b/tests/root/svgimg.pdf Binary files differnew file mode 100644 index 00000000..cacbd855 --- /dev/null +++ b/tests/root/svgimg.pdf diff --git a/tests/root/svgimg.svg b/tests/root/svgimg.svg new file mode 100644 index 00000000..10e035b6 --- /dev/null +++ b/tests/root/svgimg.svg @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + height="60" + width="60" + _SVGFile__filename="oldscale/apps/warning.svg" + version="1.0" + y="0" + x="0" + id="svg1" + sodipodi:version="0.32" + inkscape:version="0.41" + sodipodi:docname="exclamation.svg" + sodipodi:docbase="/home/danny/work/icons/primary/scalable/actions"> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0000000" + inkscape:pageshadow="2" + inkscape:zoom="7.5136000" + inkscape:cx="42.825186" + inkscape:cy="24.316071" + inkscape:window-width="1020" + inkscape:window-height="691" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:current-layer="svg1" /> + <defs + id="defs3"> + <linearGradient + id="linearGradient1160"> + <stop + style="stop-color: #000000;stop-opacity: 1.0;" + id="stop1161" + offset="0" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + id="stop1162" + offset="1" /> + </linearGradient> + <linearGradient + xlink:href="#linearGradient1160" + id="linearGradient1163" /> + </defs> + <metadata + id="metadata12"> + <RDF + id="RDF13"> + <Work + about="" + id="Work14"> + <title + id="title15">Part of the Flat Icon Collection (Thu Aug 26 14:31:40 2004)</title> + <description + id="description17" /> + <subject + id="subject18"> + <Bag + id="Bag19"> + <li + id="li20" /> + </Bag> + </subject> + <publisher + id="publisher21"> + <Agent + about="" + id="Agent22"> + <title + id="title23" /> + </Agent> + </publisher> + <creator + id="creator24"> + <Agent + about="" + id="Agent25"> + <title + id="title26">Danny Allen</title> + </Agent> + </creator> + <rights + id="rights28"> + <Agent + about="" + id="Agent29"> + <title + id="title30">Danny Allen</title> + </Agent> + </rights> + <date + id="date32" /> + <format + id="format33">image/svg+xml</format> + <type + id="type35" + resource="http://purl.org/dc/dcmitype/StillImage" /> + <license + id="license36" + resource="http://creativecommons.org/licenses/LGPL/2.1/"> + <date + id="date37" /> + </license> + <language + id="language38">en</language> + </Work> + </RDF> + <rdf:RDF + id="RDF40"> + <cc:Work + rdf:about="" + id="Work41"> + <dc:format + id="format42">image/svg+xml</dc:format> + <dc:type + id="type44" + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="g2099"> + <path + style="color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:8.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none" + d="M 55.311891,51.920745 L 4.6880989,51.920744 L 29.999995,8.0792542 L 55.311891,51.920745 z " + id="path1724" /> + <path + style="color:#000000;fill:#ffe940;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.1250010;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none" + d="M 55.311891,51.920745 L 4.6880989,51.920744 L 29.999995,8.0792542 L 55.311891,51.920745 z " + id="path1722" /> + <path + style="font-size:12.000000;font-weight:900;fill:none;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:8.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" + d="M 34.944960,10.779626 L 34.944960,33.186510 C 34.944960,34.752415 34.501979,36.081368 33.616007,37.173380 C 32.750636,38.265402 31.545298,38.811408 29.999995,38.811408 C 28.475302,38.811408 27.269965,38.265402 26.383993,37.173380 C 25.498020,36.060767 25.055030,34.731804 25.055030,33.186510 L 25.055030,10.779626 C 25.055030,9.1931155 25.498020,7.8641562 26.383993,6.7927462 C 27.269965,5.7007332 28.475302,5.1547262 29.999995,5.1547262 C 31.009593,5.1547262 31.885265,5.4019740 32.627010,5.8964706 C 33.389356,6.3909681 33.966274,7.0709005 34.357752,7.9362696 C 34.749221,8.7810349 34.944960,9.7288200 34.944960,10.779626 z " + id="path1099" /> + <path + style="font-size:12.000000;font-weight:900;fill:#e71c02;fill-opacity:1.0000000;stroke:none;stroke-width:3.1249981;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1.0000000" + d="M 29.999995,3.5986440 C 28.102272,3.5986440 26.318514,4.3848272 25.156245,5.8173940 C 24.028906,7.1806889 23.499995,8.9087770 23.499995,10.786144 L 23.499995,33.192394 C 23.499995,35.036302 24.050685,36.772771 25.156245,38.161144 C 26.318514,39.593721 28.102273,40.379893 29.999995,40.379894 C 31.913354,40.379894 33.697195,39.576736 34.843745,38.129894 C 35.959941,36.754118 36.499995,35.052976 36.499995,33.192394 L 36.499995,10.786144 C 36.499995,9.5413010 36.276626,8.3551469 35.781245,7.2861440 C 35.278844,6.1755772 34.477762,5.2531440 33.468745,4.5986440 C 32.454761,3.9226545 31.264694,3.5986439 29.999995,3.5986440 z " + id="path835" + sodipodi:nodetypes="cccccccccccc" /> + <path + style="color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:5.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none" + d="M 36.506243,49.901522 C 36.506243,53.492972 33.591442,56.407773 29.999991,56.407773 C 26.408541,56.407773 23.493739,53.492972 23.493739,49.901522 C 23.493739,46.310071 26.408541,43.395270 29.999991,43.395270 C 33.591442,43.395270 36.506243,46.310071 36.506243,49.901522 z " + id="path1727" /> + <path + style="color:#000000;fill:#e71c02;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.1250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;marker:none;marker-start:none;marker-mid:none;marker-end:none" + d="M 36.506243,49.901522 C 36.506243,53.492972 33.591442,56.407773 29.999991,56.407773 C 26.408541,56.407773 23.493739,53.492972 23.493739,49.901522 C 23.493739,46.310071 26.408541,43.395270 29.999991,43.395270 C 33.591442,43.395270 36.506243,46.310071 36.506243,49.901522 z " + id="path1725" /> + </g> +</svg> diff --git a/tests/root/testtheme/layout.html b/tests/root/testtheme/layout.html new file mode 100644 index 00000000..81372be0 --- /dev/null +++ b/tests/root/testtheme/layout.html @@ -0,0 +1,5 @@ +{% extends "basic/layout.html" %} +{% block extrahead %} +<meta name="testopt" content="{{ theme_testopt }}" /> +{{ super() }} +{% endblock %} diff --git a/tests/root/testtheme/static/staticimg.png b/tests/root/testtheme/static/staticimg.png Binary files differnew file mode 100644 index 00000000..1081dc14 --- /dev/null +++ b/tests/root/testtheme/static/staticimg.png diff --git a/tests/root/testtheme/static/statictmpl.html_t b/tests/root/testtheme/static/statictmpl.html_t new file mode 100644 index 00000000..4ab292b4 --- /dev/null +++ b/tests/root/testtheme/static/statictmpl.html_t @@ -0,0 +1,2 @@ +<!-- testing static templates --> +<html><project>{{ project|e }}</project></html> diff --git a/tests/root/testtheme/theme.conf b/tests/root/testtheme/theme.conf new file mode 100644 index 00000000..a8776737 --- /dev/null +++ b/tests/root/testtheme/theme.conf @@ -0,0 +1,7 @@ +[theme] +inherit = basic +stylesheet = default.css +pygments_style = emacs + +[options] +testopt = optdefault diff --git a/tests/root/ziptheme.zip b/tests/root/ziptheme.zip Binary files differnew file mode 100644 index 00000000..8a246ed9 --- /dev/null +++ b/tests/root/ziptheme.zip diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 752c0725..8e011438 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -3,7 +3,7 @@ test_autodoc ~~~~~~~~~~~~ - Test the autodoc extension. This tests mainly the RstGenerator; the auto + Test the autodoc extension. This tests mainly the Documenters; the auto directives are tested in a test source file translated by test_build. :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. @@ -14,16 +14,18 @@ from util import * from docutils.statemachine import ViewList -from sphinx.ext.autodoc import RstGenerator, cut_lines, between +from sphinx.ext.autodoc import AutoDirective, Documenter, add_documenter, \ + ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL def setup_module(): - global app, lid, options, gen + global app, lid, options, directive app = TestApp() app.builder.env.app = app app.connect('autodoc-process-docstring', process_docstring) app.connect('autodoc-process-signature', process_signature) + app.connect('autodoc-skip-member', skip_member) options = Struct( inherited_members = False, @@ -33,27 +35,27 @@ def setup_module(): synopsis = '', platform = '', deprecated = False, + members = [], + member_order = 'alphabetic', + exclude_members = set(), ) - gen = TestGenerator(options, app) + directive = Struct( + env = app.builder.env, + genopt = options, + result = ViewList(), + warn = warnfunc, + filename_set = set(), + ) def teardown_module(): app.cleanup() -class TestGenerator(RstGenerator): - """Generator that handles warnings without a reporter.""" - - def __init__(self, options, app): - self.options = options - self.env = app.builder.env - self.lineno = 42 - self.filename_set = set() - self.warnings = [] - self.result = ViewList() +_warnings = [] - def warn(self, msg): - self.warnings.append(msg) +def warnfunc(msg): + _warnings.append(msg) processed_docstrings = [] @@ -71,62 +73,73 @@ def process_signature(app, what, name, obj, options, args, retann): return '42', None -def test_resolve_name(): - # for modules - assert gen.resolve_name('module', 'test_autodoc') == \ - ('test_autodoc', 'test_autodoc', [], None, None) - assert gen.resolve_name('module', 'test.test_autodoc') == \ - ('test.test_autodoc', 'test.test_autodoc', [], None, None) +def skip_member(app, what, name, obj, skip, options): + if name.startswith('_'): + return True + if name == 'skipmeth': + return True - assert gen.resolve_name('module', 'test(arg)') == \ - ('test', 'test', [], None, None) - assert 'ignoring signature arguments' in gen.warnings[0] - del gen.warnings[:] + +def test_parse_name(): + def verify(objtype, name, result): + inst = AutoDirective._registry[objtype](directive, name) + assert inst.parse_name() + assert (inst.modname, inst.objpath, inst.args, inst.retann) == result + + # for modules + verify('module', 'test_autodoc', ('test_autodoc', [], None, None)) + verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None)) + verify('module', 'test(arg)', ('test', [], 'arg', None)) + assert 'signature arguments' in _warnings[0] + del _warnings[:] # for functions/classes - assert gen.resolve_name('function', 'util.raises') == \ - ('util.raises', 'util', ['raises'], None, None) - assert gen.resolve_name('function', 'util.raises(exc) -> None') == \ - ('util.raises', 'util', ['raises'], 'exc', ' -> None') - gen.env.autodoc_current_module = 'util' - assert gen.resolve_name('function', 'raises') == \ - ('raises', 'util', ['raises'], None, None) - gen.env.autodoc_current_module = None - gen.env.currmodule = 'util' - assert gen.resolve_name('function', 'raises') == \ - ('raises', 'util', ['raises'], None, None) - assert gen.resolve_name('class', 'TestApp') == \ - ('TestApp', 'util', ['TestApp'], None, None) + verify('function', 'util.raises', ('util', ['raises'], None, None)) + verify('function', 'util.raises(exc) -> None', + ('util', ['raises'], 'exc', 'None')) + directive.env.autodoc_current_module = 'util' + verify('function', 'raises', ('util', ['raises'], None, None)) + directive.env.autodoc_current_module = None + directive.env.currmodule = 'util' + verify('function', 'raises', ('util', ['raises'], None, None)) + verify('class', 'TestApp', ('util', ['TestApp'], None, None)) # for members - gen.env.currmodule = 'foo' - assert gen.resolve_name('method', 'util.TestApp.cleanup') == \ - ('util.TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None) - gen.env.currmodule = 'util' - gen.env.currclass = 'Foo' - gen.env.autodoc_current_class = 'TestApp' - assert gen.resolve_name('method', 'cleanup') == \ - ('cleanup', 'util', ['TestApp', 'cleanup'], None, None) - assert gen.resolve_name('method', 'TestApp.cleanup') == \ - ('TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None) + directive.env.currmodule = 'foo' + verify('method', 'util.TestApp.cleanup', + ('util', ['TestApp', 'cleanup'], None, None)) + directive.env.currmodule = 'util' + directive.env.currclass = 'Foo' + directive.env.autodoc_current_class = 'TestApp' + verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None)) + verify('method', 'TestApp.cleanup', + ('util', ['TestApp', 'cleanup'], None, None)) # and clean up - gen.env.currmodule = None - gen.env.currclass = None - gen.env.autodoc_current_class = None + directive.env.currmodule = None + directive.env.currclass = None + directive.env.autodoc_current_class = None def test_format_signature(): + def formatsig(objtype, name, obj, args, retann): + inst = AutoDirective._registry[objtype](directive, name) + inst.fullname = name + inst.doc_as_attr = False # for class objtype + inst.object = obj + inst.args = args + inst.retann = retann + return inst.format_signature() + # no signatures for modules - assert gen.format_signature('module', 'test', None, None, None) == '' + assert formatsig('module', 'test', None, None, None) == '' # test for functions def f(a, b, c=1, **d): pass - assert gen.format_signature('function', 'f', f, None, None) == '(a, b, c=1, **d)' - assert gen.format_signature('function', 'f', f, 'a, b, c, d', None) == \ - '(a, b, c, d)' - assert gen.format_signature('function', 'f', f, None, ' -> None') == \ + assert formatsig('function', 'f', f, None, None) == '(a, b, c=1, **d)' + assert formatsig('function', 'f', f, 'a, b, c, d', None) == '(a, b, c, d)' + assert formatsig('function', 'f', f, None, 'None') == \ '(a, b, c=1, **d) -> None' # test for classes @@ -136,15 +149,15 @@ def test_format_signature(): pass # no signature for classes without __init__ for C in (D, E): - assert gen.format_signature('class', 'D', C, None, None) == '' + assert formatsig('class', 'D', C, None, None) == '' class F: def __init__(self, a, b=None): pass class G(F, object): pass for C in (F, G): - assert gen.format_signature('class', 'C', C, None, None) == '(a, b=None)' - assert gen.format_signature('class', 'C', D, 'a, b', ' -> X') == '(a, b) -> X' + assert formatsig('class', 'C', C, None, None) == '(a, b=None)' + assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X' # test for methods class H: @@ -152,26 +165,29 @@ def test_format_signature(): pass def foo2(b, *c): pass - assert gen.format_signature('method', 'H.foo', H.foo1, None, None) == '(b, *c)' - assert gen.format_signature('method', 'H.foo', H.foo1, 'a', None) == '(a)' - assert gen.format_signature('method', 'H.foo', H.foo2, None, None) == '(b, *c)' + assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)' + assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)' + assert formatsig('method', 'H.foo', H.foo2, None, None) == '(b, *c)' # test exception handling - raises(RuntimeError, gen.format_signature, 'function', 'int', int, None, None) + raises(TypeError, formatsig, 'function', 'int', int, None, None) # test processing by event handler - assert gen.format_signature('method', 'bar', H.foo1, None, None) == '42' + assert formatsig('method', 'bar', H.foo1, None, None) == '42' def test_get_doc(): - def getdocl(*args): - # strip the empty line at the end - return list(gen.get_doc(*args))[:-1] + def getdocl(objtype, obj, encoding=None): + inst = AutoDirective._registry[objtype](directive, 'tmp') + inst.object = obj + ds = inst.get_doc(encoding) + # for testing purposes, concat them and strip the empty line at the end + return sum(ds, [])[:-1] # objects without docstring def f(): pass - assert getdocl('function', 'f', f) == [] + assert getdocl('function', f) == [] # standard function, diverse docstring styles... def f(): @@ -181,7 +197,7 @@ def test_get_doc(): Docstring """ for func in (f, g): - assert getdocl('function', 'f', func) == ['Docstring'] + assert getdocl('function', func) == ['Docstring'] # first line vs. other lines indentation def f(): @@ -190,29 +206,29 @@ def test_get_doc(): Other lines """ - assert getdocl('function', 'f', f) == ['First line', '', 'Other', ' lines'] + assert getdocl('function', f) == ['First line', '', 'Other', ' lines'] # charset guessing (this module is encoded in utf-8) def f(): """Döcstring""" - assert getdocl('function', 'f', f) == [u'Döcstring'] + assert getdocl('function', f) == [u'Döcstring'] # already-unicode docstrings must be taken literally def f(): u"""Döcstring""" - assert getdocl('function', 'f', f) == [u'Döcstring'] + assert getdocl('function', f) == [u'Döcstring'] # class docstring: depends on config value which one is taken class C: """Class docstring""" def __init__(self): """Init docstring""" - gen.env.config.autoclass_content = 'class' - assert getdocl('class', 'C', C) == ['Class docstring'] - gen.env.config.autoclass_content = 'init' - assert getdocl('class', 'C', C) == ['Init docstring'] - gen.env.config.autoclass_content = 'both' - assert getdocl('class', 'C', C) == ['Class docstring', '', 'Init docstring'] + directive.env.config.autoclass_content = 'class' + assert getdocl('class', C) == ['Class docstring'] + directive.env.config.autoclass_content = 'init' + assert getdocl('class', C) == ['Init docstring'] + directive.env.config.autoclass_content = 'both' + assert getdocl('class', C) == ['Class docstring', '', 'Init docstring'] class D: """Class docstring""" @@ -224,26 +240,33 @@ def test_get_doc(): """ # Indentation is normalized for 'both' - assert getdocl('class', 'D', D) == ['Class docstring', '', 'Init docstring', - '', 'Other', ' lines'] + assert getdocl('class', D) == ['Class docstring', '', 'Init docstring', + '', 'Other', ' lines'] + + +def test_docstring_processing(): + def process(objtype, name, obj): + inst = AutoDirective._registry[objtype](directive, name) + inst.object = obj + inst.fullname = name + return list(inst.process_doc(inst.get_doc())) class E: def __init__(self): """Init docstring""" # docstring processing by event handler - assert getdocl('class', 'bar', E) == ['Init docstring', '', '42'] + assert process('class', 'bar', E) == ['Init docstring', '', '42', ''] - -def test_docstring_processing_functions(): - lid = app.connect('autodoc-process-docstring', cut_lines(1, 1, ['function'])) + lid = app.connect('autodoc-process-docstring', + cut_lines(1, 1, ['function'])) def f(): """ first line second line third line """ - assert list(gen.get_doc('function', 'f', f)) == ['second line', ''] + assert process('function', 'f', f) == ['second line', ''] app.disconnect(lid) lid = app.connect('autodoc-process-docstring', between('---', ['function'])) @@ -255,118 +278,173 @@ def test_docstring_processing_functions(): --- third line """ - assert list(gen.get_doc('function', 'f', f)) == ['second line', ''] + assert process('function', 'f', f) == ['second line', ''] app.disconnect(lid) +def test_new_documenter(): + class MyDocumenter(ModuleLevelDocumenter): + objtype = 'integer' + directivetype = 'data' + priority = 100 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return isinstance(member, int) + + def document_members(self, all_members=False): + return + + add_documenter(MyDocumenter) + + def assert_result_contains(item, objtype, name, **kw): + inst = AutoDirective._registry[objtype](directive, name) + inst.generate(**kw) + #print '\n'.join(directive.result) + assert len(_warnings) == 0, _warnings + assert item in directive.result + del directive.result[:] + + options.members = ['integer'] + assert_result_contains('.. data:: integer', 'module', 'test_autodoc') + + def test_generate(): - def assert_warns(warn_str, *args): - gen.generate(*args) - assert len(gen.result) == 0, gen.result - assert len(gen.warnings) == 1, gen.warnings - assert warn_str in gen.warnings[0], gen.warnings - del gen.warnings[:] - - def assert_works(*args): - gen.generate(*args) - assert gen.result - assert len(gen.warnings) == 0, gen.warnings - del gen.result[:] - - def assert_processes(items, *args): + def assert_warns(warn_str, objtype, name, **kw): + inst = AutoDirective._registry[objtype](directive, name) + inst.generate(**kw) + assert len(directive.result) == 0, directive.result + assert len(_warnings) == 1, _warnings + assert warn_str in _warnings[0], _warnings + del _warnings[:] + + def assert_works(objtype, name, **kw): + inst = AutoDirective._registry[objtype](directive, name) + inst.generate(**kw) + assert directive.result + assert len(_warnings) == 0, _warnings + del directive.result[:] + + def assert_processes(items, objtype, name, **kw): del processed_docstrings[:] del processed_signatures[:] - assert_works(*args) - assert set(processed_docstrings) | set(processed_signatures) == set(items) + assert_works(objtype, name, **kw) + assert set(processed_docstrings) | set(processed_signatures) == \ + set(items) - def assert_result_contains(item, *args): - gen.generate(*args) - print '\n'.join(gen.result) - assert len(gen.warnings) == 0, gen.warnings - assert item in gen.result - del gen.result[:] + def assert_result_contains(item, objtype, name, **kw): + inst = AutoDirective._registry[objtype](directive, name) + inst.generate(**kw) + #print '\n'.join(directive.result) + assert len(_warnings) == 0, _warnings + assert item in directive.result + del directive.result[:] + + options.members = [] # no module found? assert_warns("import for autodocumenting 'foobar'", - 'function', 'foobar', None, None) + 'function', 'foobar', more_content=None) # importing assert_warns("import/find module 'test_foobar'", - 'module', 'test_foobar', None, None) + 'module', 'test_foobar', more_content=None) # attributes missing assert_warns("import/find function 'util.foobar'", - 'function', 'util.foobar', None, None) + 'function', 'util.foobar', more_content=None) # test auto and given content mixing - gen.env.currmodule = 'test_autodoc' - assert_result_contains(' Function.', 'method', 'Class.meth', [], None) + directive.env.currmodule = 'test_autodoc' + assert_result_contains(' Function.', 'method', 'Class.meth') add_content = ViewList() add_content.append('Content.', '', 0) - assert_result_contains(' Function.', 'method', 'Class.meth', [], add_content) - assert_result_contains(' Content.', 'method', 'Class.meth', [], add_content) + assert_result_contains(' Function.', 'method', + 'Class.meth', more_content=add_content) + assert_result_contains(' Content.', 'method', + 'Class.meth', more_content=add_content) # test check_module - gen.generate('function', 'raises', None, None, check_module=True) - assert len(gen.result) == 0 + inst = FunctionDocumenter(directive, 'raises') + inst.generate(check_module=True) + assert len(directive.result) == 0 # assert that exceptions can be documented - assert_works('exception', 'test_autodoc.CustomEx', ['__all__'], None) - assert_works('exception', 'test_autodoc.CustomEx', [], None) + assert_works('exception', 'test_autodoc.CustomEx', all_members=True) + assert_works('exception', 'test_autodoc.CustomEx') # test diverse inclusion settings for members - should = [('class', 'Class')] - assert_processes(should, 'class', 'Class', [], None) - should.extend([('method', 'Class.meth')]) - assert_processes(should, 'class', 'Class', ['meth'], None) - should.extend([('attribute', 'Class.prop')]) - assert_processes(should, 'class', 'Class', ['__all__'], None) + should = [('class', 'test_autodoc.Class')] + assert_processes(should, 'class', 'Class') + should.extend([('method', 'test_autodoc.Class.meth')]) + options.members = ['meth'] + options.exclude_members = set(['excludemeth']) + assert_processes(should, 'class', 'Class') + should.extend([('attribute', 'test_autodoc.Class.prop'), + ('attribute', 'test_autodoc.Class.attr'), + ('attribute', 'test_autodoc.Class.docattr'), + ('attribute', 'test_autodoc.Class.udocattr')]) + options.members = ALL + assert_processes(should, 'class', 'Class') options.undoc_members = True - should.append(('method', 'Class.undocmeth')) - assert_processes(should, 'class', 'Class', ['__all__'], None) + should.append(('method', 'test_autodoc.Class.undocmeth')) + assert_processes(should, 'class', 'Class') options.inherited_members = True - should.append(('method', 'Class.inheritedmeth')) - assert_processes(should, 'class', 'Class', ['__all__'], None) + should.append(('method', 'test_autodoc.Class.inheritedmeth')) + assert_processes(should, 'class', 'Class') + options.members = [] # test module flags - assert_result_contains('.. module:: test_autodoc', 'module', - 'test_autodoc', [], None) + assert_result_contains('.. module:: test_autodoc', 'module', 'test_autodoc') options.synopsis = 'Synopsis' - assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc', [], None) + assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc') options.deprecated = True - assert_result_contains(' :deprecated:', 'module', 'test_autodoc', [], None) + assert_result_contains(' :deprecated:', 'module', 'test_autodoc') options.platform = 'Platform' - assert_result_contains(' :platform: Platform', 'module', 'test_autodoc', [], None) + assert_result_contains(' :platform: Platform', 'module', 'test_autodoc') # test if __all__ is respected for modules - assert_result_contains('.. class:: Class', 'module', 'test_autodoc', - ['__all__'], None) + options.members = ALL + assert_result_contains('.. class:: Class', 'module', 'test_autodoc') try: - assert_result_contains('.. exception:: CustomEx', 'module', 'test_autodoc', - ['__all__'], None) + assert_result_contains('.. exception:: CustomEx', + 'module', 'test_autodoc') except AssertionError: pass else: assert False, 'documented CustomEx which is not in __all__' # test noindex flag + options.members = [] options.noindex = True - assert_result_contains(' :noindex:', 'module', 'test_autodoc', [], None) - assert_result_contains(' :noindex:', 'class', 'Base', [], None) + assert_result_contains(' :noindex:', 'module', 'test_autodoc') + assert_result_contains(' :noindex:', 'class', 'Base') # okay, now let's get serious about mixing Python and C signature stuff assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict', - ['__all__'], None) + all_members=True) + + # test inner class handling + assert_processes([('class', 'test_autodoc.Outer'), + ('class', 'test_autodoc.Outer.Inner'), + ('method', 'test_autodoc.Outer.Inner.meth')], + 'class', 'Outer', all_members=True) + + # test generation for C modules (which have no source file) + directive.env.currmodule = 'time' + assert_processes([('function', 'time.asctime')], 'function', 'asctime') + assert_processes([('function', 'time.asctime')], 'function', 'asctime') # --- generate fodder ------------ __all__ = ['Class'] +integer = 1 + class CustomEx(Exception): """My custom exception.""" def f(self): """Exception method.""" - class Base(object): def inheritedmeth(self): """Inherited function.""" @@ -380,9 +458,28 @@ class Class(Base): def undocmeth(self): pass - @property + def skipmeth(self): + """Method that should be skipped.""" + + def excludemeth(self): + """Method that should be excluded.""" + + # should not be documented + skipattr = 'foo' + + #: should be documented -- süß + attr = 'bar' + def prop(self): """Property.""" + # stay 2.4 compatible (docstring!) + prop = property(prop, doc="Property.") + + docattr = 'baz' + """should likewise be documented -- süß""" + + udocattr = 'quux' + u"""should be documented as well - süß""" class CustomDict(dict): """Docstring.""" @@ -392,3 +489,16 @@ def function(foo, *args, **kwds): Return spam. """ pass + + +class Outer(object): + """Foo""" + + class Inner(object): + """Foo""" + + def meth(self): + """Foo""" + + # should be documented as an alias + factory = dict diff --git a/tests/test_build.py b/tests/test_build.py index 888063a9..7b33dc09 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -10,6 +10,7 @@ """ import os +import re import sys import difflib import htmlentitydefs @@ -19,29 +20,41 @@ from subprocess import Popen, PIPE from util import * from etree13 import ElementTree as ET -from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder -from sphinx.latexwriter import LaTeXTranslator +try: + import pygments +except ImportError: + pygments = None + +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.builders.latex import LaTeXBuilder +from sphinx.writers.latex import LaTeXTranslator + + +def teardown_module(): + (test_root / '_build').rmtree(True) html_warnfile = StringIO() latex_warnfile = StringIO() ENV_WARNINGS = """\ -WARNING: %(root)s/images.txt:9: Image file not readable: foo.png -WARNING: %(root)s/images.txt:23: Nonlocal image URI found: http://www.python.org/logo.png -WARNING: %(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading included \ -file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option +%(root)s/images.txt:9: WARNING: image file not readable: foo.png +%(root)s/images.txt:23: WARNING: nonlocal image URI found: \ +http://www.python.org/logo.png +%(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading \ +included file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option +%(root)s/includes.txt:56: WARNING: download file not readable: nonexisting.png """ HTML_WARNINGS = ENV_WARNINGS + """\ -WARNING: %(root)s/images.txt:: no matching candidate for image URI u'foo.*' -WARNING: %(root)s/markup.txt:: invalid index entry u'' -WARNING: %(root)s/markup.txt:: invalid pair index entry u'' -WARNING: %(root)s/markup.txt:: invalid pair index entry u'keyword; ' +%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.*' +%(root)s/markup.txt:: WARNING: invalid index entry u'' +%(root)s/markup.txt:: WARNING: invalid pair index entry u'' +%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' """ LATEX_WARNINGS = ENV_WARNINGS + """\ -WARNING: None:: no matching candidate for image URI u'foo.*' +None:None: WARNING: no matching candidate for image URI u'foo.*' WARNING: invalid pair index entry u'' """ @@ -50,18 +63,27 @@ HTML_XPATH = { ".//img[@src='_images/img.png']": '', ".//img[@src='_images/img1.png']": '', ".//img[@src='_images/simg.png']": '', + ".//object[@data='_images/svgimg.svg']": '', + ".//embed[@src='_images/svgimg.svg']": '', }, 'subdir/images.html': { ".//img[@src='../_images/img1.png']": '', + ".//img[@src='../_images/rimg.png']": '', + }, + 'subdir/includes.html': { + ".//pre/span": 'line 1', + ".//pre/span": 'line 2', + ".//a[@href='../_downloads/img.png']": '', }, 'includes.html': { - ".//pre/span[@class='s']": u'üöä', ".//pre": u'Max Strauß', + ".//a[@href='_downloads/img.png']": '', + ".//a[@href='_downloads/img1.png']": '', }, 'autodoc.html': { ".//dt[@id='test_autodoc.Class']": '', - ".//dt[@id='test_autodoc.function']/em": '**kwds', - ".//dd": 'Return spam.', + ".//dt[@id='test_autodoc.function']/em": r'\*\*kwds', + ".//dd": r'Return spam\.', }, 'markup.html': { ".//meta[@name='author'][@content='Me']": '', @@ -69,23 +91,48 @@ HTML_XPATH = { ".//a[@href='contents.html#ref1']": '', ".//div[@id='label']": '', ".//span[@class='option']": '--help', + ".//p": 'A global substitution.', + ".//p": 'In HTML.', + ".//p": 'In both.', + ".//p": 'Always present', }, 'desc.html': { ".//dt[@id='mod.Cls.meth1']": '', ".//dt[@id='errmod.Error']": '', ".//a[@href='#mod.Cls']": '', + ".//dl[@class='userdesc']": '', + ".//dt[@id='userdescrole-myobj']": '', + ".//a[@href='#userdescrole-myobj']": '', }, 'contents.html': { ".//meta[@name='hc'][@content='hcval']": '', - #".//td[@class='label']": '[Ref1]', # docutils 0.5 only + ".//meta[@name='testopt'][@content='testoverride']": '', + #".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only ".//td[@class='label']": '', ".//li[@class='toctree-l1']/a": 'Testing various markup', ".//li[@class='toctree-l2']/a": 'Admonitions', ".//title": 'Sphinx <Tests>', ".//div[@class='footer']": 'Georg Brandl & Team', + ".//a[@href='http://python.org/']": '', + }, + '_static/statictmpl.html': { + ".//project": 'Sphinx <Tests>', }, } +if pygments: + HTML_XPATH['includes.html'].update({ + ".//pre/span[@class='s']": u'üöä', + ".//div[@class='inc-pyobj1 highlight-text']/div/pre": + r'^class Foo:\n pass\n\s*$', + ".//div[@class='inc-pyobj2 highlight-text']/div/pre": + r'^ def baz\(\):\n pass\n\s*$', + ".//div[@class='inc-lines highlight-text']/div/pre": + r'^class Foo:\n pass\nclass Bar:\n$', + ".//div[@class='inc-startend highlight-text']/div/pre": + ur'^foo = u"Including Unicode characters: üöä"\n$', + }) + class NslessParser(ET.XMLParser): """XMLParser that throws away namespaces in tag names.""" @@ -101,7 +148,27 @@ class NslessParser(ET.XMLParser): return name -@with_app(buildername='html', warning=html_warnfile, cleanenv=True) +def check_xpath(etree, fname, path, check): + nodes = list(etree.findall(path)) + assert nodes != [], ('did not find any node matching xpath ' + '%r in file %s' % (path, fname)) + if hasattr(check, '__call__'): + check(nodes) + elif not check: + # only check for node presence + pass + else: + rex = re.compile(check) + for node in nodes: + if node.text and rex.search(node.text): + break + else: + assert False, ('%r not found in any node matching ' + 'path %s in %s: %r' % (check, path, fname, + [node.text for node in nodes])) + +@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True, + tags=['testtag']) def test_html(app): app.builder.build_all() html_warnings = html_warnfile.getvalue().replace(os.sep, '/') @@ -114,18 +181,8 @@ def test_html(app): parser = NslessParser() parser.entity.update(htmlentitydefs.entitydefs) etree = ET.parse(os.path.join(app.outdir, fname), parser) - for path, text in paths.iteritems(): - nodes = list(etree.findall(path)) - assert nodes != [] - if not text: - # only check for node presence - continue - for node in nodes: - if node.text and text in node.text: - break - else: - assert False, ('%r not found in any node matching ' - 'path %s in %s' % (text, path, fname)) + for path, check in paths.iteritems(): + yield check_xpath, etree, fname, path, check @with_app(buildername='latex', warning=latex_warnfile, cleanenv=True) @@ -137,6 +194,8 @@ def test_latex(app): assert latex_warnings == latex_warnings_exp, 'Warnings don\'t match:\n' + \ '\n'.join(difflib.ndiff(latex_warnings_exp.splitlines(), latex_warnings.splitlines())) + # file from latex_additional_files + assert (app.outdir / 'svgimg.svg').isfile() # only run latex if all needed packages are there def kpsetest(filename): @@ -155,12 +214,15 @@ def test_latex(app): return True if kpsetest('article.sty') is None: - print >>sys.stderr, 'info: not running latex, it doesn\'t seem to be installed' + print >>sys.stderr, \ + 'info: not running latex, it doesn\'t seem to be installed' return - for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', 'amsmath.sty', - 'framed.sty', 'color.sty', 'fancyvrb.sty', 'threeparttable.sty']: + for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', + 'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty', + 'threeparttable.sty']: if not kpsetest(filename): - print >>sys.stderr, 'info: not running latex, the %s package doesn\'t ' \ + print >>sys.stderr, \ + 'info: not running latex, the %s package doesn\'t ' \ 'seem to be installed' % filename return @@ -169,8 +231,8 @@ def test_latex(app): os.chdir(app.outdir) try: try: - p = Popen(['pdflatex', '--interaction=nonstopmode', 'SphinxTests.tex'], - stdout=PIPE, stderr=PIPE) + p = Popen(['pdflatex', '--interaction=nonstopmode', + 'SphinxTests.tex'], stdout=PIPE, stderr=PIPE) except OSError, err: pass # most likely pdflatex was not found else: @@ -184,14 +246,26 @@ def test_latex(app): # just let the remaining ones run for now -@with_app(buildername='linkcheck', cleanenv=True) +@with_app(buildername='pickle') +def test_pickle(app): + app.builder.build_all() + +@with_app(buildername='linkcheck') def test_linkcheck(app): app.builder.build_all() -@with_app(buildername='text', cleanenv=True) +@with_app(buildername='text') def test_text(app): app.builder.build_all() +@with_app(buildername='htmlhelp') +def test_htmlhelp(app): + app.builder.build_all() + +@with_app(buildername='qthelp') +def test_qthelp(app): + app.builder.build_all() + @with_app(buildername='changes', cleanenv=True) def test_changes(app): app.builder.build_all() diff --git a/tests/test_config.py b/tests/test_config.py index bc5ab8a0..b3aa4eea 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -15,7 +15,8 @@ from util import * from sphinx.application import ExtensionError -@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True'}) +@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True', + 'latex_elements.docclass': 'scrartcl'}) def test_core_config(app): cfg = app.config @@ -26,6 +27,7 @@ def test_core_config(app): # overrides assert cfg.master_doc == 'master' + assert cfg.latex_elements['docclass'] == 'scrartcl' # simple default values assert 'exclude_dirs' not in cfg.__dict__ @@ -34,7 +36,7 @@ def test_core_config(app): # complex default values assert 'html_title' not in cfg.__dict__ - assert cfg.html_title == 'Sphinx <Tests> v0.4alpha1 documentation' + assert cfg.html_title == 'Sphinx <Tests> v0.6alpha1 documentation' # complex default values mustn't raise for valuename in cfg.config_values: diff --git a/tests/test_env.py b/tests/test_env.py index 390c6999..a06656d6 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -12,7 +12,8 @@ from util import * from sphinx.environment import BuildEnvironment -from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.builders.latex import LaTeXBuilder app = env = None warnings = [] @@ -21,14 +22,14 @@ def setup_module(): global app, env app = TestApp(srcdir='(temp)') env = BuildEnvironment(app.srcdir, app.doctreedir, app.config) - env.set_warnfunc(warnings.append) + env.set_warnfunc(lambda *args: warnings.append(args)) def teardown_module(): app.cleanup() def warning_emitted(file, text): for warning in warnings: - if file+':' in warning and text in warning: + if len(warning) == 2 and file+':' in warning[1] and text in warning[0]: return True return False @@ -36,8 +37,7 @@ def warning_emitted(file, text): # afford to not run update() in the setup but in its own test def test_first_update(): - it = env.update(app.config, app.srcdir, app.doctreedir, app) - msg = it.next() + msg, num, it = env.update(app.config, app.srcdir, app.doctreedir, app) assert msg.endswith('%d added, 0 changed, 0 removed' % len(env.found_docs)) docnames = set() for docname in it: # the generator does all the work @@ -45,44 +45,51 @@ def test_first_update(): assert docnames == env.found_docs == set(env.all_docs) def test_images(): - assert warning_emitted('images.txt', 'Image file not readable: foo.png') - assert warning_emitted('images.txt', 'Nonlocal image URI found: ' + assert warning_emitted('images.txt', 'image file not readable: foo.png') + assert warning_emitted('images.txt', 'nonlocal image URI found: ' 'http://www.python.org/logo.png') tree = env.get_doctree('images') app._warning.reset() htmlbuilder = StandaloneHTMLBuilder(app, env) htmlbuilder.post_process_images(tree) - assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1] - assert set(htmlbuilder.images.keys()) == set(['subdir/img.png', 'img.png', - 'subdir/simg.png']) - assert set(htmlbuilder.images.values()) == set(['img.png', 'img1.png', - 'simg.png']) + assert "no matching candidate for image URI u'foo.*'" in \ + app._warning.content[-1] + assert set(htmlbuilder.images.keys()) == \ + set(['subdir/img.png', 'img.png', 'subdir/simg.png', 'svgimg.svg']) + assert set(htmlbuilder.images.values()) == \ + set(['img.png', 'img1.png', 'simg.png', 'svgimg.svg']) app._warning.reset() latexbuilder = LaTeXBuilder(app, env) latexbuilder.post_process_images(tree) - assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1] - assert set(latexbuilder.images.keys()) == set(['subdir/img.png', 'subdir/simg.png', - 'img.png', 'img.pdf']) - assert set(latexbuilder.images.values()) == set(['img.pdf', 'img.png', - 'img1.png', 'simg.png']) + assert "no matching candidate for image URI u'foo.*'" in \ + app._warning.content[-1] + assert set(latexbuilder.images.keys()) == \ + set(['subdir/img.png', 'subdir/simg.png', 'img.png', 'img.pdf', + 'svgimg.pdf']) + assert set(latexbuilder.images.values()) == \ + set(['img.pdf', 'img.png', 'img1.png', 'simg.png', 'svgimg.pdf']) def test_second_update(): # delete, add and "edit" (change saved mtime) some files and update again env.all_docs['contents'] = 0 root = path(app.srcdir) - (root / 'images.txt').unlink() + # important: using "autodoc" because it is the last one to be included in + # the contents.txt toctree; otherwise section numbers would shift + (root / 'autodoc.txt').unlink() (root / 'new.txt').write_text('New file\n========\n') - it = env.update(app.config, app.srcdir, app.doctreedir, app) - msg = it.next() - assert '1 added, 1 changed, 1 removed' in msg + msg, num, it = env.update(app.config, app.srcdir, app.doctreedir, app) + assert '1 added, 3 changed, 1 removed' in msg docnames = set() for docname in it: docnames.add(docname) - assert docnames == set(['contents', 'new']) - assert 'images' not in env.all_docs - assert 'images' not in env.found_docs + # "includes" and "images" are in there because they contain references + # to nonexisting downloadable or image files, which are given another + # chance to exist + assert docnames == set(['contents', 'new', 'includes', 'images']) + assert 'autodoc' not in env.all_docs + assert 'autodoc' not in env.found_docs def test_object_inventory(): refs = env.descrefs diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py new file mode 100644 index 00000000..ea1f25f1 --- /dev/null +++ b/tests/test_highlighting.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +""" + test_highlighting + ~~~~~~~~~~~~~~~~~ + + Test the Pygments highlighting bridge. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from util import * + +try: + import pygments +except ImportError: + from nose.plugins.skip import SkipTest + raise SkipTest('pygments not available') + +from pygments.lexer import RegexLexer +from pygments.token import Text, Name +from pygments.formatters.html import HtmlFormatter + +from sphinx.highlighting import PygmentsBridge + + +class MyLexer(RegexLexer): + name = 'testlexer' + + tokens = { + 'root': [ + ('a', Name), + ('b', Text), + ], + } + + +class MyFormatter(HtmlFormatter): + def format(self, tokensource, outfile): + outfile.write('test') + + +class ComplainOnUnhighlighted(PygmentsBridge): + def unhighlighted(self, source): + raise AssertionError("should highlight %r" % source) + + +@with_app() +def test_add_lexer(app): + app.add_lexer('test', MyLexer()) + + bridge = PygmentsBridge('html') + ret = bridge.highlight_block('ab', 'test') + assert '<span class="n">a</span>b' in ret + +def test_detect_interactive(): + bridge = ComplainOnUnhighlighted('html') + blocks = [ + """ + >>> testing() + True + """, + ] + for block in blocks: + ret = bridge.highlight_block(block.lstrip(), 'python') + assert ret.startswith("<div class=\"highlight\">") + +def test_set_formatter(): + PygmentsBridge.html_formatter = MyFormatter + try: + bridge = PygmentsBridge('html') + ret = bridge.highlight_block('foo', 'python') + assert ret == 'test' + finally: + PygmentsBridge.html_formatter = HtmlFormatter diff --git a/tests/test_markup.py b/tests/test_markup.py index 169cdca9..28ec17f5 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -17,11 +17,13 @@ from docutils import frontend, utils, nodes from docutils.parsers import rst from sphinx import addnodes -from sphinx.htmlwriter import HTMLWriter, SmartyPantsHTMLTranslator -from sphinx.latexwriter import LaTeXWriter, LaTeXTranslator +from sphinx.util import texescape +from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator +from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator def setup_module(): global app, settings, parser + texescape.init() # otherwise done by the latex builder app = TestApp(cleanenv=True) optparser = frontend.OptionParser( components=(rst.Parser, HTMLWriter, LaTeXWriter)) @@ -79,38 +81,39 @@ def test_inline(): # correct interpretation of code with whitespace _html = ('<p><tt class="docutils literal"><span class="pre">' 'code</span> <span class="pre">sample</span></tt></p>') - verify('``code sample``', _html, '\\code{code sample}') - verify(':samp:`code sample`', _html, '\\samp{code sample}') + yield verify, '``code sample``', _html, '\\code{code sample}' + yield verify, ':samp:`code sample`', _html, '\\samp{code sample}' # interpolation of braces in samp and file roles (HTML only) - verify(':samp:`a{b}c`', + yield (verify, ':samp:`a{b}c`', '<p><tt class="docutils literal"><span class="pre">a</span>' - '<em><span class="pre">b</span></em><span class="pre">c</span></tt></p>', + '<em><span class="pre">b</span></em>' + '<span class="pre">c</span></tt></p>', '\\samp{abc}') # interpolation of arrows in menuselection - verify(':menuselection:`a --> b`', + yield (verify, ':menuselection:`a --> b`', u'<p><em>a \N{TRIANGULAR BULLET} b</em></p>', '\\emph{a \\(\\rightarrow\\) b}') # non-interpolation of dashes in option role - verify_re(':option:`--with-option`', - '<p><em( class="xref")?>--with-option</em></p>$', - r'\\emph{\\texttt{-{-}with-option}}$') + yield (verify_re, ':option:`--with-option`', + '<p><em( class="xref")?>--with-option</em></p>$', + r'\\emph{\\texttt{-{-}with-option}}$') # verify smarty-pants quotes - verify('"John"', '<p>“John”</p>', "``John''") + yield verify, '"John"', '<p>“John”</p>', "``John''" # ... but not in literal text - verify('``"John"``', + yield (verify, '``"John"``', '<p><tt class="docutils literal"><span class="pre">' '"John"</span></tt></p>', '\\code{"John"}') def test_latex_escaping(): # correct escaping in normal mode - verify(u'Γ\\\\∞$', None, ur'\(\Gamma\)\textbackslash{}\(\infty\)\$') + yield verify, u'Γ\\\\∞$', None, ur'\(\Gamma\)\textbackslash{}\(\infty\)\$' # in verbatim code fragments - verify(u'::\n\n @Γ\\∞$[]', None, + yield (verify, u'::\n\n @Γ\\∞$[]', None, u'\\begin{Verbatim}[commandchars=@\\[\\]]\n' u'@PYGZat[]@(@Gamma@)\\@(@infty@)@$@PYGZlb[]@PYGZrb[]\n' u'\\end{Verbatim}') diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 4c2d0f3e..ae001eb6 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -70,6 +70,7 @@ def test_do_prompt(): assert d['k5'] == 'no' raises(AssertionError, qs.do_prompt, d, 'k6', 'Q6', validator=qs.boolean) + @with_tempdir def test_quickstart_defaults(tempdir): answers = { @@ -86,29 +87,31 @@ def test_quickstart_defaults(tempdir): ns = {} execfile(conffile, ns) assert ns['extensions'] == [] - assert ns['templates_path'] == ['.templates'] + assert ns['templates_path'] == ['_templates'] assert ns['source_suffix'] == '.rst' assert ns['master_doc'] == 'index' assert ns['project'] == 'Sphinx Test' assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y') assert ns['version'] == '0.1' assert ns['release'] == '0.1' - assert ns['html_static_path'] == ['.static'] + assert ns['html_static_path'] == ['_static'] assert ns['latex_documents'] == [ ('index', 'SphinxTest.tex', 'Sphinx Test Documentation', 'Georg Brandl', 'manual')] - assert (tempdir / '.static').isdir() - assert (tempdir / '.templates').isdir() + assert (tempdir / '_static').isdir() + assert (tempdir / '_templates').isdir() assert (tempdir / 'index.rst').isfile() assert (tempdir / 'Makefile').isfile() + assert (tempdir / 'make.bat').isfile() + @with_tempdir def test_quickstart_all_answers(tempdir): answers = { 'Root path': tempdir, 'Separate source and build': 'y', - 'Name prefix for templates': '_', + 'Name prefix for templates': '.', 'Project name': 'STASI\xe2\x84\xa2', 'Author name': 'Wolfgang Sch\xc3\xa4uble & G. Beckstein', 'Project version': '2.0', @@ -118,7 +121,13 @@ def test_quickstart_all_answers(tempdir): 'autodoc': 'y', 'doctest': 'yes', 'intersphinx': 'no', + 'todo': 'n', + 'coverage': 'no', + 'pngmath': 'N', + 'jsmath': 'no', + 'ifconfig': 'no', 'Create Makefile': 'no', + 'Create Windows command file': 'no', } qs.raw_input = mock_raw_input(answers, needanswer=True) qs.TERM_ENCODING = 'utf-8' @@ -129,7 +138,7 @@ def test_quickstart_all_answers(tempdir): ns = {} execfile(conffile, ns) assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] - assert ns['templates_path'] == ['_templates'] + assert ns['templates_path'] == ['.templates'] assert ns['source_suffix'] == '.txt' assert ns['master_doc'] == 'contents' assert ns['project'] == u'STASI™' @@ -137,12 +146,12 @@ def test_quickstart_all_answers(tempdir): time.strftime('%Y') assert ns['version'] == '2.0' assert ns['release'] == '2.0.1' - assert ns['html_static_path'] == ['_static'] + assert ns['html_static_path'] == ['.static'] assert ns['latex_documents'] == [ ('contents', 'STASI.tex', u'STASI™ Documentation', ur'Wolfgang Schäuble \& G. Beckstein', 'manual')] assert (tempdir / 'build').isdir() - assert (tempdir / 'source' / '_static').isdir() - assert (tempdir / 'source' / '_templates').isdir() + assert (tempdir / 'source' / '.static').isdir() + assert (tempdir / 'source' / '.templates').isdir() assert (tempdir / 'source' / 'contents.txt').isfile() diff --git a/tests/test_theming.py b/tests/test_theming.py new file mode 100644 index 00000000..349a9ce4 --- /dev/null +++ b/tests/test_theming.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" + test_theming + ~~~~~~~~~~~~ + + Test the Theme class. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import zipfile + +from util import * + +from sphinx.theming import Theme, ThemeError + + +@with_app(confoverrides={'html_theme': 'ziptheme', + 'html_theme_options.testopt': 'foo'}) +def test_theme_api(app): + cfg = app.config + + # test Theme class API + assert set(Theme.themes.keys()) == \ + set(['basic', 'default', 'sphinxdoc', 'traditional', + 'testtheme', 'ziptheme']) + assert Theme.themes['testtheme'][1] is None + assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile) + + # test Theme instance API + theme = app.builder.theme + assert theme.name == 'ziptheme' + assert theme.themedir_created + themedir = theme.themedir + assert theme.base.name == 'basic' + assert len(theme.get_dirchain()) == 2 + + # direct setting + assert theme.get_confstr('theme', 'stylesheet') == 'custom.css' + # inherited setting + assert theme.get_confstr('options', 'nosidebar') == 'false' + # nonexisting setting + assert theme.get_confstr('theme', 'foobar', 'def') == 'def' + raises(ThemeError, theme.get_confstr, 'theme', 'foobar') + + # options API + raises(ThemeError, theme.get_options, {'nonexisting': 'foo'}) + options = theme.get_options(cfg.html_theme_options) + assert options['testopt'] == 'foo' + assert options['nosidebar'] == 'false' + + # cleanup temp directories + theme.cleanup() + assert not os.path.exists(themedir) diff --git a/tests/util.py b/tests/util.py index 2cd2c031..4bb6a653 100644 --- a/tests/util.py +++ b/tests/util.py @@ -19,7 +19,8 @@ except ImportError: # functools is new in 2.4 wraps = lambda f: (lambda w: w) -from sphinx import application, builder +from sphinx import application +from sphinx.ext.autodoc import AutoDirective from path import path @@ -29,7 +30,7 @@ from nose import tools __all__ = [ 'test_root', 'raises', 'raises_msg', 'Struct', - 'ListOutput', 'TestApp', 'with_app', + 'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'path', 'with_tempdir', 'write_file', 'sprint', ] @@ -97,12 +98,14 @@ class TestApp(application.Sphinx): """ def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None, - buildername='html', confoverrides=None, status=None, warning=None, - freshenv=None, confname='conf.py', cleanenv=False): + buildername='html', confoverrides=None, + status=None, warning=None, freshenv=None, + warningiserror=None, tags=None, + confname='conf.py', cleanenv=False): application.CONFIG_FILENAME = confname - self.cleanup_trees = [] + self.cleanup_trees = [test_root / 'generated'] if srcdir is None: srcdir = test_root @@ -134,12 +137,15 @@ class TestApp(application.Sphinx): warning = ListOutput('stderr') if freshenv is None: freshenv = False + if warningiserror is None: + warningiserror = False application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides, status, warning, - freshenv) + freshenv, warningiserror, tags) def cleanup(self, doctrees=False): + AutoDirective._registry.clear() for tree in self.cleanup_trees: shutil.rmtree(tree, True) @@ -153,10 +159,26 @@ def with_app(*args, **kwargs): @wraps(func) def deco(*args2, **kwargs2): app = TestApp(*args, **kwargs) - try: - func(app, *args2, **kwargs2) - finally: - app.cleanup() + func(app, *args2, **kwargs2) + # don't execute cleanup if test failed + app.cleanup() + return deco + return generator + + +def gen_with_app(*args, **kwargs): + """ + Make a TestApp with args and kwargs, pass it to the test and clean up + properly. + """ + def generator(func): + @wraps(func) + def deco(*args2, **kwargs2): + app = TestApp(*args, **kwargs) + for item in func(app, *args2, **kwargs2): + yield item + # don't execute cleanup if test failed + app.cleanup() return deco return generator diff --git a/utils/check_sources.py b/utils/check_sources.py index ad808883..7fe0cbb7 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -30,7 +30,7 @@ def checker(*suffixes, **kwds): name_mail_re = r'[\w ]+(<.*?>)?' -copyright_re = re.compile(r'^ :copyright: Copyright 200\d(-200\d)?' +copyright_re = re.compile(r'^ :copyright: Copyright 200\d(-200\d)? ' r'by %s(, %s)*[,.]$' % (name_mail_re, name_mail_re)) license_re = re.compile(r" :license: (.*?).\n") @@ -56,7 +56,7 @@ def check_syntax(fn, lines): def check_style_and_encoding(fn, lines): encoding = 'ascii' for lno, line in enumerate(lines): - if len(line) > 90: + if len(line) > 81: yield lno+1, "line too long" if lno < 2: co = coding_re.search(line) @@ -64,9 +64,9 @@ def check_style_and_encoding(fn, lines): encoding = co.group(1) if line.strip().startswith('#'): continue - m = not_ix_re.search(line) - if m: - yield lno+1, '"' + m.group() + '"' + #m = not_ix_re.search(line) + #if m: + # yield lno+1, '"' + m.group() + '"' if is_const_re.search(line): yield lno+1, 'using == None/True/False' try: @@ -142,7 +142,7 @@ def check_fileheader(fn, lines): yield 0, "no correct copyright info" -@checker('.py', '.html') +@checker('.py', '.html', '.rst') def check_whitespace_and_spelling(fn, lines): for lno, line in enumerate(lines): if "\t" in line: @@ -154,7 +154,7 @@ def check_whitespace_and_spelling(fn, lines): yield lno+1, '"%s" used' % word -bad_tags = ('<b>', '<i>', '<u>', '<s>', '<strike>' +bad_tags = ('<u>', '<s>', '<strike>' '<center>', '<big>', '<small>', '<font') @checker('.html') diff --git a/utils/reindent.py b/utils/reindent.py index e6ee8287..c499f671 100755 --- a/utils/reindent.py +++ b/utils/reindent.py @@ -8,7 +8,8 @@ -d (--dryrun) Dry run. Analyze, but don't make any changes to files. -r (--recurse) Recurse. Search for all .py files in subdirectories too. -B (--no-backup) Don't write .bak backup files. --v (--verbose) Verbose. Print informative msgs; else only names of changed files. +-v (--verbose) Verbose. Print informative msgs; else only names of \ +changed files. -h (--help) Help. Print this usage information and exit. Change Python (.py) files to use 4-space indents and no hard tab characters. @@ -118,7 +119,8 @@ def check(file): if dryrun: print "But this is a dry run, so leaving it alone." else: - print "reindented", file, (dryrun and "(dry run => not really)" or "") + print "reindented", file, \ + (dryrun and "(dry run => not really)" or "") if not dryrun: if not no_backup: bak = file + ".bak" |
