diff --git a/CHANGES b/CHANGES index f1fb982b8..a30d490e7 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,35 @@ Release 1.1 (in development) Release 1.0.2 (in development) ============================== +* Allow breaking long signatures, continuing with backlash-escaped + newlines. + +* Fix unwanted styling of C domain references (because of a namespace + clash with Pygments styles). + +* Allow references to PEPs and RFCs with explicit anchors. + +* #471: Fix LaTeX references to figures. + +* #482: When doing a non-exact search, match only the given type + of object. + +* #481: Apply non-exact search for Python reference targets with + ``.name`` for modules too. + +* #484: Fix crash when duplicating a parameter in an info field list. + +* #487: Fix setting the default role to one provided by the + ``oldcmarkup`` extension. + +* #488: Fix crash when json-py is installed, which provides a + ``json`` module but is incompatible to simplejson. + +* #480: Fix handling of target naming in intersphinx. + +* #486: Fix removal of ``!`` for all cross-reference roles. + + Release 1.0.1 (Jul 27, 2010) ============================ diff --git a/EXAMPLES b/EXAMPLES index 44c511e9e..778b235e1 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -97,6 +97,7 @@ Documentation using the sphinxdoc theme * Satchmo: http://www.satchmoproject.com/docs/svn/ * Sphinx: http://sphinx.pocoo.org/ * Sqlkit: http://sqlkit.argolinux.org/ +* Tau: http://www.tango-controls.org/static/tau/latest/doc/html/index.html * Total Open Station: http://tops.berlios.de/ * WebFaction: http://docs.webfaction.com/ diff --git a/doc/builders.rst b/doc/builders.rst index 6e90ccc62..80203e759 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -255,11 +255,11 @@ All serialization builders outputs one file per source file and a few special files. They also copy the reST source files in the directory ``_sources`` under the output directory. -The :class:`PickleHTMLBuilder` is a builtin subclass that implements the pickle +The :class:`.PickleHTMLBuilder` is a builtin subclass that implements the pickle serialization interface. The files per source file have the extensions of -:attr:`~SerializingHTMLBuilder.out_suffix`, and are arranged in directories +:attr:`~.SerializingHTMLBuilder.out_suffix`, and are arranged in directories just as the source files are. They unserialize to a dictionary (or dictionary like structure) with these keys: @@ -290,7 +290,7 @@ like structure) with these keys: The special files are located in the root output directory. They are: -:attr:`SerializingHTMLBuilder.globalcontext_filename` +:attr:`.SerializingHTMLBuilder.globalcontext_filename` A pickled dict with these keys: ``project``, ``copyright``, ``release``, ``version`` @@ -309,7 +309,7 @@ The special files are located in the root output directory. They are: ``titles`` A dictionary of all documents' titles, as HTML strings. -:attr:`SerializingHTMLBuilder.searchindex_filename` +:attr:`.SerializingHTMLBuilder.searchindex_filename` An index that can be used for searching the documentation. It is a pickled list with these entries: diff --git a/doc/conf.py b/doc/conf.py index 299f321ac..b3a1cda79 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -64,6 +64,10 @@ man_pages = [ 'template generator', '', 1), ] +# We're not using intersphinx right now, but if we did, this would be part of +# the mapping: +intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)} + # -- Extension interface ------------------------------------------------------- diff --git a/doc/config.rst b/doc/config.rst index bf8ad3c2d..e0fbeb46e 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -346,12 +346,12 @@ Project information A boolean that decides whether module names are prepended to all :term:`object` names (for object types where a "module" of some kind is - defined), e.g. for :rst:dir:`function` directives. Default is ``True``. + defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``. .. confval:: show_authors - A boolean that decides whether :rst:dir:`moduleauthor` and :rst:dir:`sectionauthor` - directives produce any output in the built files. + A boolean that decides whether :rst:dir:`codeauthor` and + :rst:dir:`sectionauthor` directives produce any output in the built files. .. confval:: modindex_common_prefix @@ -388,6 +388,8 @@ Options for HTML output These options influence HTML as well as HTML Help output, and other builders that use Sphinx' HTMLWriter class. +.. XXX document html_context + .. confval:: html_theme The "theme" that the HTML output should use. See the :doc:`section about @@ -553,19 +555,6 @@ that use Sphinx' HTMLWriter class. This will render the template ``customdownload.html`` as the page ``download.html``. - .. note:: - - Earlier versions of Sphinx had a value called :confval:`html_index` which - was a clumsy way of controlling the content of the "index" document. If - you used this feature, migrate it by adding an ``'index'`` key to this - setting, with your custom template as the value, and in your custom - template, use :: - - {% extend "defindex.html" %} - {% block tables %} - ... old template content ... - {% endblock %} - .. confval:: html_domain_indices If true, generate domain-specific indices in addition to the general index. diff --git a/doc/domains.rst b/doc/domains.rst index c64930a24..8cd7a0c7d 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -52,10 +52,19 @@ flag ``:noindex:``. An example using a Python domain directive:: .. py:function:: spam(eggs) ham(eggs) - :noindex: Spam or ham the foo. +This describes the two Python functions ``spam`` and ``ham``. (Note that when +signatures become too long, you can break them if you add a backslash to lines +that are continued in the next line. Example:: + + .. py:function:: filterwarnings(action, message='', category=Warning, \ + module='', lineno=0, append=False) + :noindex: + +(This example also shows how to use the ``:noindex:`` flag.) + The domains also provide roles that link back to these object descriptions. For example, to link to one of the functions described in the example above, you could say :: @@ -138,11 +147,12 @@ declarations: .. rst:directive:: .. py:currentmodule:: name This directive tells Sphinx that the classes, functions etc. documented from - here are in the given module (like :rst:dir:`py:module`), but it will not create - index entries, an entry in the Global Module Index, or a link target for - :rst:role:`mod`. This is helpful in situations where documentation for things in - a module is spread over multiple files or sections -- one location has the - :rst:dir:`py:module` directive, the others only :rst:dir:`py:currentmodule`. + here are in the given module (like :rst:dir:`py:module`), but it will not + create index entries, an entry in the Global Module Index, or a link target + for :rst:role:`py:mod`. This is helpful in situations where documentation + for things in a module is spread over multiple files or sections -- one + location has the :rst:dir:`py:module` directive, the others only + :rst:dir:`py:currentmodule`. The following directives are provided for module and class contents: @@ -363,6 +373,9 @@ dot, this order is reversed. For example, in the documentation of Python's :mod:`codecs` module, ``:py:func:`open``` always refers to the built-in function, while ``:py:func:`.open``` refers to :func:`codecs.open`. +A similar heuristic is used to determine whether the name is an attribute of the +currently documented class. + Also, if the name is prefixed with a dot, and no exact match is found, the target is taken as a suffix and all object names with that suffix are searched. For example, ``:py:meth:`.TarFile.close``` references the @@ -370,8 +383,9 @@ searched. For example, ``:py:meth:`.TarFile.close``` references the ``tarfile``. Since this can get ambiguous, if there is more than one possible match, you will get a warning from Sphinx. -A similar heuristic is used to determine whether the name is an attribute of the -currently documented class. +Note that you can combine the ``~`` and ``.`` prefixes: +``:py:meth:`~.TarFile.close``` will reference the ``tarfile.TarFile.close()`` +method, but the visible link caption will only be ``close()``. .. _c-domain: diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index 402dd72f0..2717c6a8e 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -210,7 +210,7 @@ the following public API: standard Sphinx roles (see :ref:`xref-syntax`). This method is also available under the deprecated alias - :meth:`add_description_unit`. + ``add_description_unit``. .. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None, objname='') @@ -272,6 +272,8 @@ the following public API: This allows to auto-document new types of objects. See the source of the autodoc module for examples on how to subclass :class:`Documenter`. + .. XXX add real docs for Documenter and subclassing + .. versionadded:: 0.6 .. method:: Sphinx.add_autodoc_attrgetter(type, getter) diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index bd725cfaa..813331015 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -27,20 +27,21 @@ two locations for documentation, while at the same time avoiding auto-generated-looking pure API documentation. :mod:`autodoc` provides several directives that are versions of the usual -:rst:dir:`module`, :rst:dir:`class` and so forth. On parsing time, they import the -corresponding module and extract the docstring of the given objects, inserting -them into the page source under a suitable :rst:dir:`module`, :rst:dir:`class` etc. -directive. +:rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they +import the corresponding module and extract the docstring of the given objects, +inserting them into the page source under a suitable :rst:dir:`py:module`, +:rst:dir:`py:class` etc. directive. .. note:: - Just as :rst:dir:`class` respects the current :rst:dir:`module`, :rst:dir:`autoclass` - will also do so, and likewise with :rst:dir:`method` and :rst:dir:`class`. + Just as :rst:dir:`py:class` respects the current :rst:dir:`py:module`, + :rst:dir:`autoclass` will also do so. Likewise, :rst:dir:`automethod` will + respect the current :rst:dir:`py:class`. .. rst:directive:: automodule - autoclass - autoexception + autoclass + autoexception Document a module, class or exception. All three directives will by default only insert the docstring of the object itself:: @@ -127,23 +128,24 @@ directive. .. versionadded:: 0.4 - * The :rst:dir:`automodule`, :rst:dir:`autoclass` and :rst: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 :rst:dir:`automodule`, this will be inserted for every class that is - documented in the module). + * The :rst:dir:`automodule`, :rst:dir:`autoclass` and + :rst: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 :rst:dir:`automodule`, this + will be inserted for every class that is documented in the module). .. versionadded:: 0.4 * All autodoc directives support the ``noindex`` flag option that has the - same effect as for standard :rst:dir:`function` etc. directives: no index - entries are generated for the documented object (and all autodocumented - members). + same effect as for standard :rst:dir:`py:function` etc. directives: no + index entries are generated for the documented object (and all + autodocumented members). .. versionadded:: 0.4 * :rst:dir:`automodule` also recognizes the ``synopsis``, ``platform`` and - ``deprecated`` options that the standard :rst:dir:`module` directive supports. + ``deprecated`` options that the standard :rst:dir:`py:module` directive + supports. .. versionadded:: 0.5 @@ -213,8 +215,8 @@ There are also new config values that you can set: ``"class"`` Only the class' docstring is inserted. This is the default. You can - still document ``__init__`` as a separate method using :rst:dir:`automethod` - or the ``members`` option to :rst:dir:`autoclass`. + still document ``__init__`` as a separate method using + :rst:dir:`automethod` or the ``members`` option to :rst:dir:`autoclass`. ``"both"`` Both the class' and the ``__init__`` method's docstring are concatenated and inserted. diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst index 76388a94c..cdd017917 100644 --- a/doc/ext/inheritance.rst +++ b/doc/ext/inheritance.rst @@ -17,7 +17,7 @@ It adds this directive: 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 :rst:dir:`module`). + in the currently described module (see :rst:dir:`py: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 diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst index bb2a5a8c4..7997472a7 100644 --- a/doc/ext/intersphinx.rst +++ b/doc/ext/intersphinx.rst @@ -84,7 +84,7 @@ linking: To add links to modules and objects in the Python standard library documentation, use:: - intersphinx_mapping = {'python': ('http://docs.python.org/', None)} + intersphinx_mapping = {'python': ('http://docs.python.org/3.2', None)} This will download the corresponding :file:`objects.inv` file from the Internet and generate links to the pages under the given URI. The downloaded @@ -94,12 +94,12 @@ linking: A second example, showing the meaning of a non-``None`` value of the second tuple item:: - intersphinx_mapping = {'python': ('http://docs.python.org/', + intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-inv.txt')} This will read the inventory from :file:`python-inv.txt` in the source directory, but still generate links to the pages under - ``http://docs.python.org/``. It is up to you to update the inventory file as + ``http://docs.python.org/3.2``. It is up to you to update the inventory file as new objects are added to the Python documentation. .. confval:: intersphinx_cache_limit diff --git a/doc/ext/math.rst b/doc/ext/math.rst index b9f6ab12b..f2896c39c 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -17,15 +17,15 @@ if possible, reuse that support too. .. note:: - :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. + :mod:`.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 further translation is necessary when building LaTeX output. -:mod:`mathbase` defines these new markup elements: +:mod:`.mathbase` defines these new markup elements: .. rst:role:: math diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index bb1ed68ec..4b704228f 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -260,7 +260,7 @@ in a different style: .. rst:role:: samp A piece of literal text, such as code. Within the contents, you can use - curly braces to indicate a "variable" part, as in :rst:dir:`file`. For + curly braces to indicate a "variable" part, as in :rst:role:`file`. For example, in ``:samp:`print 1+{variable}```, the part ``variable`` would be emphasized. @@ -274,13 +274,15 @@ The following roles generate external links: A reference to a Python Enhancement Proposal. This generates appropriate index entries. The text "PEP *number*\ " is generated; in the HTML output, - this text is a hyperlink to an online copy of the specified PEP. + this text is a hyperlink to an online copy of the specified PEP. You can + link to a specific section by saying ``:pep:`number#anchor```. .. rst:role:: rfc A reference to an Internet Request for Comments. This generates appropriate index entries. The text "RFC *number*\ " is generated; in the HTML output, - this text is a hyperlink to an online copy of the specified RFC. + this text is a hyperlink to an online copy of the specified RFC. You can + link to a specific section by saying ``:rfc:`number#anchor```. Note that there are no special roles for including hyperlinks as you can use diff --git a/doc/markup/para.rst b/doc/markup/para.rst index be06f6365..ecc6b4a6b 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -42,15 +42,25 @@ units as well as normal text: Example:: .. versionadded:: 2.5 - The `spam` parameter. + The *spam* parameter. Note that there must be no blank line between the directive head and the explanation; this is to make these blocks visually continuous in the markup. .. rst:directive:: .. versionchanged:: version - Similar to :rst:dir:`versionadded`, but describes when and what changed in the named - feature in some way (new parameters, changed side effects, etc.). + Similar to :rst:dir:`versionadded`, but describes when and what changed in + the named feature in some way (new parameters, changed side effects, etc.). + +.. rst:directive:: .. deprecated:: vesion + + Similar to :rst:dir:`versionchanged`, but describes when the feature was + deprecated. An explanation can also be given, for example to inform the + reader what should be used instead. Example:: + + .. deprecated:: 3.1 + Use :func:`spam` instead. + -------------- diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst index 474427d72..2c0a418a2 100644 --- a/doc/markup/toctree.rst +++ b/doc/markup/toctree.rst @@ -151,7 +151,7 @@ The special document names (and pages generated for them) are: :ref:`object descriptions `, and from :rst:dir:`index` directives. - The module index contains one entry per :rst:dir:`module` directive. + The Python module index contains one entry per :rst:dir:`py:module` directive. The search page contains a form that uses the generated JSON search index and JavaScript to full-text search the generated documents for search words; it diff --git a/doc/templating.rst b/doc/templating.rst index 6880663d3..193a90bd9 100644 --- a/doc/templating.rst +++ b/doc/templating.rst @@ -21,10 +21,10 @@ No. You have several other options: configuration value accordingly. * You can :ref:`write a custom builder ` that derives from - :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine - of choice. + :class:`~sphinx.builders.html.StandaloneHTMLBuilder` and calls your template + engine of choice. -* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces +* You can use the :class:`~sphinx.builders.html.PickleHTMLBuilder` that produces pickle files with the page contents, and postprocess them using a custom tool, or use them in your Web application. @@ -261,9 +261,9 @@ in the future. .. 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``. + The value of the builder's :attr:`~.SerializingHTMLBuilder.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 diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 6c03b8e5f..48c44178d 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -32,6 +32,7 @@ except AttributeError: # RE to strip backslash escapes +nl_escape_re = re.compile(r'\\\n') strip_backslash_re = re.compile(r'\\(?=[^\\])') @@ -57,10 +58,12 @@ class ObjectDescription(Directive): """ Retrieve the signatures to document from the directive arguments. By default, signatures are given as arguments, one per line. + + Backslash-escaping of newlines is supported. """ + lines = nl_escape_re.sub('', self.arguments[0]).split('\n') # remove backslashes to support (dummy) escapes; helps Vim highlighting - return [strip_backslash_re.sub('', sig.strip()) - for sig in self.arguments[0].split('\n')] + return [strip_backslash_re.sub('', line.strip()) for line in lines] def handle_signature(self, sig, signode): """ diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fc8086995..cd87bfbda 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -356,6 +356,9 @@ class PyModule(Directive): env.domaindata['py']['modules'][modname] = \ (env.docname, self.options.get('synopsis', ''), self.options.get('platform', ''), 'deprecated' in self.options) + # make a duplicate entry in 'objects' to facilitate searching for the + # module in PythonDomain.find_obj() + env.domaindata['py']['objects'][modname] = (env.docname, 'module') targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] @@ -544,7 +547,7 @@ class PythonDomain(Domain): if fn == docname: del self.data['modules'][modname] - def find_obj(self, env, modname, classname, name, type, searchorder=0): + def find_obj(self, env, modname, classname, name, type, searchmode=0): """ Find a Python object for "name", perhaps using the given module and/or classname. Returns a list of (name, object entry) tuples. @@ -560,22 +563,31 @@ class PythonDomain(Domain): matches = [] newname = None - if searchorder == 1: - if modname and classname and \ - modname + '.' + classname + '.' + name in objects: - newname = modname + '.' + classname + '.' + name - elif modname and modname + '.' + name in objects: - newname = modname + '.' + name - elif name in objects: - newname = name - else: - # "fuzzy" searching mode - searchname = '.' + name - matches = [(name, objects[name]) for name in objects - if name.endswith(searchname)] + if searchmode == 1: + objtypes = self.objtypes_for_role(type) + if modname and classname: + fullname = modname + '.' + classname + '.' + name + if fullname in objects and objects[fullname][1] in objtypes: + newname = fullname + if not newname: + if modname and modname + '.' + name in objects and \ + objects[modname + '.' + name][1] in objtypes: + newname = modname + '.' + name + elif name in objects and objects[name][1] in objtypes: + newname = name + else: + # "fuzzy" searching mode + searchname = '.' + name + matches = [(name, objects[name]) for name in objects + if name.endswith(searchname) + and objects[name][1] in objtypes] else: + # NOTE: searching for exact match, object type is not considered if name in objects: newname = name + elif type == 'mod': + # only exact matches allowed for modules + return [] elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif modname and modname + '.' + name in objects: @@ -597,33 +609,35 @@ class PythonDomain(Domain): def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): - if (type == 'mod' or - type == 'obj' and target in self.data['modules']): - docname, synopsis, platform, deprecated = \ - self.data['modules'].get(target, ('','','', '')) - if not docname: - return None - else: - title = '%s%s%s' % ((platform and '(%s) ' % platform), - synopsis, - (deprecated and ' (deprecated)' or '')) - return make_refnode(builder, fromdocname, docname, - 'module-' + target, contnode, title) + modname = node.get('py:module') + clsname = node.get('py:class') + searchmode = node.hasattr('refspecific') and 1 or 0 + matches = self.find_obj(env, modname, clsname, target, + type, searchmode) + if not matches: + return None + elif len(matches) > 1: + env.warn(fromdocname, + 'more than one target found for cross-reference ' + '%r: %s' % (target, + ', '.join(match[0] for match in matches)), + node.line) + name, obj = matches[0] + + if obj[1] == 'module': + # get additional info for modules + docname, synopsis, platform, deprecated = self.data['modules'][name] + assert docname == obj[0] + title = name + if synopsis: + title += ': ' + synopsis + if deprecated: + title += _(' (deprecated)') + if platform: + title += ' (' + platform + ')' + return make_refnode(builder, fromdocname, docname, + 'module-' + name, contnode, title) else: - modname = node.get('py:module') - clsname = node.get('py:class') - searchorder = node.hasattr('refspecific') and 1 or 0 - matches = self.find_obj(env, modname, clsname, target, - type, searchorder) - if not matches: - return None - elif len(matches) > 1: - env.warn(fromdocname, - 'more than one target found for cross-reference ' - '%r: %s' % (target, - ', '.join(match[0] for match in matches)), - node.line) - name, obj = matches[0] return make_refnode(builder, fromdocname, obj[0], name, contnode, name) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 61809f330..d3ffc6bdc 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -28,7 +28,7 @@ class ReSTMarkup(ObjectDescription): """ def add_target_and_index(self, name, sig, signode): - targetname = name + '-' + self.objtype + targetname = self.objtype + '-' + name if targetname not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetname) @@ -130,8 +130,9 @@ class ReSTDomain(Domain): if (objtype, target) in objects: return make_refnode(builder, fromdocname, objects[objtype, target], - target, contnode, target) + objtype + '-' + target, + contnode, target + ' ' + objtype) def get_objects(self): for (typ, name), docname in self.data['objects'].iteritems(): - yield name, name, typ, docname, name, 1 + yield name, name, typ, docname, typ + '-' + name, 1 diff --git a/sphinx/environment.py b/sphinx/environment.py index bf255165e..a051d6361 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -65,7 +65,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 37 +ENV_VERSION = 38 default_substitutions = set([ diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index b930d8cab..a12bad256 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -""" +r""" sphinx.ext.inheritance_diagram ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 07ee24e38..251f5c55d 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -213,9 +213,20 @@ def missing_reference(app, env, node, contnode): proj, version, uri, dispname = inventory[objtype][target] newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle='(in %s v%s)' % (proj, version)) - if dispname == '-': - dispname = target - newnode.append(contnode.__class__(dispname, dispname)) + if node.get('refexplicit'): + # use whatever title was given + newnode.append(contnode) + elif dispname == '-': + # use whatever title was given, but strip prefix + title = contnode.astext() + if in_set and title.startswith(in_set+':'): + newnode.append(contnode.__class__(title[len(in_set)+1:], + title[len(in_set)+1:])) + else: + newnode.append(contnode) + else: + # else use the given display name (used for :ref:) + newnode.append(contnode.__class__(dispname, dispname)) return newnode # at least get rid of the ':' in the target if no explicit title given if in_set is not None and not node.get('refexplicit', True): diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 3aa53fd86..571d82a51 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -43,6 +43,8 @@ class OldCDirective(Directive): def old_crole(typ, rawtext, text, lineno, inliner, options={}, content=[]): env = inliner.document.settings.env + if not typ: + typ = env.config.default_role if not env.app._oldcmarkup_warned: env.warn(env.docname, WARNING_MSG) env.app._oldcmarkup_warned = True diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index c168aeffc..6d7109199 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -240,7 +240,7 @@ class PygmentsBridge(object): # no HTML styles needed return '' if self.dest == 'html': - return self.fmter[0].get_style_defs() + return self.fmter[0].get_style_defs('.highlight') else: styledefs = self.fmter[0].get_style_defs() # workaround for Pygments < 0.12 diff --git a/sphinx/roles.py b/sphinx/roles.py index d3f3c67e3..0ea0ec485 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -105,9 +105,9 @@ class XRefRole(object): classes = ['xref', domain, '%s-%s' % (domain, role)] # if the first character is a bang, don't cross-reference at all if text[0:1] == '!': - text = utils.unescape(text) + text = utils.unescape(text)[1:] if self.fix_parens: - text, tgt = self._fix_parens(env, False, text[1:], "") + text, tgt = self._fix_parens(env, False, text, "") innernode = self.innernodeclass(rawtext, text, classes=classes) return self.result_nodes(inliner.document, env, innernode, is_ref=False) @@ -173,6 +173,10 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, indexnode['entries'] = [ ('single', _('Python Enhancement Proposals!PEP %s') % text, targetid, 'PEP %s' % text)] + anchor = '' + anchorindex = text.find('#') + if anchorindex > 0: + text, anchor = text[:anchorindex], text[anchorindex:] try: pepnum = int(text) except ValueError: @@ -182,12 +186,17 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum sn = nodes.strong('PEP '+text, 'PEP '+text) - rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref+anchor, + classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, 'RFC %s' % text)] + anchor = '' + anchorindex = text.find('#') + if anchorindex > 0: + text, anchor = text[:anchorindex], text[anchorindex:] try: rfcnum = int(text) except ValueError: @@ -197,7 +206,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum sn = nodes.strong('RFC '+text, 'RFC '+text) - rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref+anchor, + classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index be8a6c15d..b5381392a 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -446,6 +446,7 @@ linkcolor=InnerLinkColor,filecolor=OuterLinkColor, menucolor=OuterLinkColor,urlcolor=OuterLinkColor, citecolor=InnerLinkColor]{hyperref} +\RequirePackage[figure,table]{hypcap} % From docutils.writers.latex2e \providecommand{\DUspan}[2]{% diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index c8e58f48a..6ce6d82bf 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -142,9 +142,12 @@ class TypedField(GroupedField): par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong) if fieldarg in types: par += nodes.Text(' (') - fieldtype = types[fieldarg] + # NOTE: using .pop() here to prevent a single type node to be + # inserted twice into the doctree, which leads to + # inconsistencies later when references are resolved + fieldtype = types.pop(fieldarg) if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): - typename = u''.join(n.astext() for n in types[fieldarg]) + typename = u''.join(n.astext() for n in fieldtype) par += self.make_xref(self.typerolename, domain, typename) else: par += fieldtype diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index b83661a7e..fda85b5e3 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -13,8 +13,10 @@ import UserString try: import json + # json-py's json module has not JSONEncoder; this will raise AttributeError + # if json-py is imported instead of the built-in json module JSONEncoder = json.JSONEncoder -except ImportError: +except (ImportError, AttributeError): try: import simplejson as json JSONEncoder = json.JSONEncoder diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 360cf40c9..de0bbdf2f 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -224,6 +224,8 @@ class LaTeXTranslator(nodes.NodeVisitor): else: self.top_sectionlevel = 1 self.next_section_ids = set() + self.next_figure_ids = set() + self.next_table_ids = set() # flags self.verbatim = None self.in_title = 0 @@ -633,7 +635,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('{|' + ('L|' * self.table.colcount) + '}\n') if self.table.longtable and self.table.caption is not None: self.body.append(u'\\caption{%s} \\\\\n' % self.table.caption) - + if self.table.caption is not None: + for id in self.next_table_ids: + self.body.append(self.hypertarget(id, anchor=False)) + self.next_table_ids.clear() if self.table.longtable: self.body.append('\\hline\n') self.body.append('\\endfirsthead\n\n') @@ -887,11 +892,15 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_figure(self, node): + ids = '' + for id in self.next_figure_ids: + ids += self.hypertarget(id, anchor=False) + self.next_figure_ids.clear() if 'width' in node 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') + self.context.append(ids + '\\end{wrapfigure}\n') else: if (not 'align' in node.attributes or node.attributes['align'] == 'center'): @@ -903,7 +912,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 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) + self.context.append(ids + align_end + '\\end{figure}\n') def depart_figure(self, node): self.body.append(self.context.pop()) @@ -983,6 +992,20 @@ class LaTeXTranslator(nodes.NodeVisitor): self.next_section_ids.add(node['refid']) self.next_section_ids.update(node['ids']) return + elif isinstance(next, nodes.figure): + # labels for figures go in the figure body, not before + if node.get('refid'): + self.next_figure_ids.add(node['refid']) + self.next_figure_ids.update(node['ids']) + return + elif isinstance(next, nodes.table): + # same for tables, but only if they have a caption + for n in node: + if isinstance(n, nodes.title): + if node.get('refid'): + self.next_table_ids.add(node['refid']) + self.next_table_ids.update(node['ids']) + return except IndexError: pass if 'refuri' in node: diff --git a/tests/root/objects.txt b/tests/root/objects.txt index 334827de2..e62f6d962 100644 --- a/tests/root/objects.txt +++ b/tests/root/objects.txt @@ -41,6 +41,10 @@ Testing object descriptions .. function:: func_without_module2() -> annotation +.. object:: long(parameter, \ + list) + another one + .. class:: TimeInt :param moo: |test| @@ -62,6 +66,8 @@ Testing object descriptions :ivar int hour: like *hour* :ivar minute: like *minute* :vartype minute: int + :param hour: Duplicate param. Should not lead to crashes. + :type hour: Duplicate type. C items diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 0c59d9cca..a685fcb61 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -39,7 +39,7 @@ http://www.python.org/logo.png reading included file u'wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png -%(root)s/objects.txt:84: WARNING: using old C markup; please migrate to \ +%(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ http://sphinx.pocoo.org/domains.html """ @@ -167,6 +167,8 @@ HTML_XPATH = { 'objects.html': [ (".//dt[@id='mod.Cls.meth1']", ''), (".//dt[@id='errmod.Error']", ''), + (".//dt/tt", r'long\(parameter,\s* list\)'), + (".//dt/tt", 'another one'), (".//a[@href='#mod.Cls'][@class='reference internal']", ''), (".//dl[@class='userdesc']", ''), (".//dt[@id='userdesc-myobj']", ''), diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py index 4f70bd205..990e35bd2 100644 --- a/tests/test_intersphinx.py +++ b/tests/test_intersphinx.py @@ -97,46 +97,58 @@ def test_missing_reference(tempdir, app): ('foo', '2.0', 'http://docs.python.org/foo.html#module-module2', '-') # create fake nodes and check referencing - contnode = nodes.emphasis('foo', 'foo') - refnode = addnodes.pending_xref('') - refnode['reftarget'] = 'module1.func' - refnode['reftype'] = 'func' - refnode['refdomain'] = 'py' - rn = missing_reference(app, app.env, refnode, contnode) + def fake_node(domain, type, target, content, **attrs): + contnode = nodes.emphasis(content, content) + node = addnodes.pending_xref('') + node['reftarget'] = target + node['reftype'] = type + node['refdomain'] = domain + node.attributes.update(attrs) + node += contnode + return node, contnode + + def reference_check(*args, **kwds): + node, contnode = fake_node(*args, **kwds) + return missing_reference(app, app.env, node, contnode) + + # check resolution when a target is found + rn = reference_check('py', 'func', 'module1.func', 'foo') assert isinstance(rn, nodes.reference) assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func' assert rn['reftitle'] == '(in foo v2.0)' - assert rn[0].astext() == 'module1.func' + assert rn[0].astext() == 'foo' # create unresolvable nodes and check None return value - refnode['reftype'] = 'foo' - assert missing_reference(app, app.env, refnode, contnode) is None - - refnode['reftype'] = 'function' - refnode['reftarget'] = 'foo.func' - assert missing_reference(app, app.env, refnode, contnode) is None + assert reference_check('py', 'foo', 'module1.func', 'foo') is None + assert reference_check('py', 'func', 'foo', 'foo') is None + assert reference_check('py', 'func', 'foo', 'foo') is None # check handling of prefixes # prefix given, target found: prefix is stripped - refnode['reftype'] = 'mod' - refnode['reftarget'] = 'py3k:module2' - rn = missing_reference(app, app.env, refnode, contnode) + rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2') assert rn[0].astext() == 'module2' + # prefix given, but not in title: nothing stripped + rn = reference_check('py', 'mod', 'py3k:module2', 'module2') + assert rn[0].astext() == 'module2' + + # prefix given, but explicit: nothing stripped + rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2', + refexplicit=True) + assert rn[0].astext() == 'py3k:module2' + # prefix given, target not found and nonexplicit title: prefix is stripped - refnode['reftarget'] = 'py3k:unknown' - refnode['refexplicit'] = False - contnode[0] = nodes.Text('py3k:unknown') - rn = missing_reference(app, app.env, refnode, contnode) + node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown', + refexplicit=False) + rn = missing_reference(app, app.env, node, contnode) assert rn is None assert contnode[0].astext() == 'unknown' # prefix given, target not found and explicit title: nothing is changed - refnode['reftarget'] = 'py3k:unknown' - refnode['refexplicit'] = True - contnode[0] = nodes.Text('py3k:unknown') - rn = missing_reference(app, app.env, refnode, contnode) + node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown', + refexplicit=True) + rn = missing_reference(app, app.env, node, contnode) assert rn is None assert contnode[0].astext() == 'py3k:unknown'