chiark / gitweb /
documentation/python, m.sphinx: support documenting raised exceptions.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 15 Sep 2019 19:34:22 +0000 (21:34 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 15 Sep 2019 19:51:35 +0000 (21:51 +0200)
15 files changed:
doc/documentation/python.rst
doc/plugins/sphinx.rst
documentation/python.py
documentation/templates/python/details-function.html
documentation/templates/python/details-property.html
documentation/test_python/content/content.Class.html
documentation/test_python/content/content.html
documentation/test_python/content/content/__init__.py
documentation/test_python/content/docs.rst
documentation/test_python/inspect_type_links/docs.rst
documentation/test_python/inspect_type_links/inspect_type_links.Foo.html [new file with mode: 0644]
documentation/test_python/inspect_type_links/inspect_type_links.html
documentation/test_python/inspect_type_links/inspect_type_links/__init__.py
documentation/test_python/test_inspect.py
plugins/m/sphinx.py

index f539bbdd1ccb7eefed14acc28848f1991424e0c8..50b7630f3def1d4aadad4cb212e8830eca1930a0 100644 (file)
@@ -1257,6 +1257,8 @@ Property                            Description
                                     cross-linked types
 :py:`function.params`               List of function parameters. See below for
                                     details.
+:py:`function.exceptions`           List of exceptions raised by this function.
+                                    See below for details.
 :py:`function.has_complex_params`   Set to :py:`True` if the parameter list
                                     should be wrapped on several lines for
                                     better readability (for example when it
@@ -1276,8 +1278,8 @@ Property                            Description
                                     :py:`False` otherwise.
 =================================== ===========================================
 
-The :py:`func.params` is a list of function parameters and their description.
-Each item has the following properties:
+The :py:`function.params` is a list of function parameters and their
+description. Each item has the following properties:
 
 .. class:: m-table m-fullwidth
 
@@ -1298,6 +1300,19 @@ In some cases (for example in case of native APIs), the parameters can't be
 introspected. In that case, the parameter list is a single entry with ``name``
 set to :py:`"..."` and the rest being empty.
 
+The :py:`function.exceptions` is a list of exceptions types and descriptions.
+Each item has the following properties:
+
+.. class:: m-table m-fullwidth
+
+=========================== ===================================================
+Property                    Description
+=========================== ===================================================
+:py:`exception.type`        Exception type
+:py:`exception.type_link`   Like :py:`exception`, but with a cross-linked type
+:py:`exception.content`     Detailed documentation
+=========================== ===================================================
+
 `Property properties`_
 ``````````````````````
 
@@ -1313,6 +1328,10 @@ Property                            Description
                                     cross-linked types
 :py:`property.summary`              Doc summary
 :py:`property.content`              Detailed documentation, if any
+:py:`property.exceptions`           List of exceptions raised when accessing
+                                    this property. Same as
+                                    :py:`function.exceptions` described in
+                                    `function properties`_.
 :py:`property.is_gettable`          If the property is gettable
 :py:`property.is_settable`          If the property is settable
 :py:`property.is_deletable`         If the property is deletable with :py:`del`
index 00486ec16a2ff7e8b00fa65a91582434ffabdfed..2c8d534be5c0ff6f8bfd3cf74f77e00d56db333a 100644 (file)
@@ -421,11 +421,14 @@ conveniently directly in the :rst:`.. py:enum::` directive via
 ------------
 
 The :rst:`.. py:function::` directive supports additional options ---
-:py:`:param <name>:` for documenting parameters and :py:`:return:` for
-documenting the return value. It's allowed to have either none or all
-parameters documented (the ``self`` parameter can be omitted), having them
-documented only partially or documenting parameters that are not present in the
-function signature will cause a warning. Example:
+:rst:`:param name:` for documenting parameters, :rst:`:raise name:` for
+documenting raised exceptions and :rst:`:return:` for documenting the return
+value. It's allowed to have either none or all parameters documented (the
+``self`` parameter can be omitted), having them documented only partially or
+documenting parameters that are not present in the function signature will
+cause a warning. Documenting one parameter multiple times causes a warning, on
+the other hand listing one exception multiple times is a valid use case.
+Example:
 
 .. code:: rst
 
@@ -435,6 +438,7 @@ function signature will cause a warning. Example:
         :param value:               Corresponding value
         :param overwrite_existing:  Overwrite existing value if already present
             in the container
+        :raise ValueError:  If the key type is not hashable
         :return:                    The inserted tuple or the existing
             key/value pair in case :p:`overwrite_existing` is not set
 
@@ -470,10 +474,11 @@ Example:
 `Properties`_
 -------------
 
-Use :rst:`.. py:property::` for documenting properties. This directive doesn't
-support any additional options besides :rst:`:summary:`. For convenience,
-properties that have just a summary can be also documented directly in the
-enclosing :rst:`.. py:class::` directive `as shown above <#classes>`__.
+Use :rst:`.. py:property::` for documenting properties. This directive supports
+the :rst:`:raise name:` option similarly as for `functions`_, plus the usual
+:rst:`:summary:`. For convenience, properties that have just a summary can be
+also documented directly in the enclosing :rst:`.. py:class::` directive
+`as shown above <#classes>`__.
 
 .. code:: rst
 
index ad21e8dbd082def96e1d535c944c4c796e310df3..ece774ee24cc8d2b88c50e7c6f2f5bc564bbc3e9 100755 (executable)
@@ -1524,7 +1524,7 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
 
         overloads = [out]
 
-    # Common path for parameter / return value docs and search
+    # Common path for parameter / exception / return value docs and search
     path_str = '.'.join(entry.path)
     for out in overloads:
         signature = '({})'.format(', '.join(['{}: {}'.format(param.name, param.type) if param.type else param.name for param in out.params]))
@@ -1568,6 +1568,16 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
                     if name not in used_params:
                         logging.warning("%s%s documents parameter %s, which isn't in the signature", path_str, signature, name)
 
+            if function_docs.get('raise'):
+                out.exceptions = []
+                for type_, content in function_docs['raise']:
+                    exception = Empty()
+                    exception.type = type_
+                    exception.type_link = make_name_link(state, entry.path, type_)
+                    exception.content = render_inline_rst(state, content)
+                    out.exceptions += [exception]
+                out.has_details = True
+
             if function_docs.get('return'):
                 try:
                     out.return_value = render_inline_rst(state, function_docs['return'])
@@ -1739,6 +1749,18 @@ def extract_property_doc(state: State, parent, entry: Empty):
     for hook in state.hooks_post_scope:
         hook(type=entry.type, path=entry.path)
 
+    # Exception docs, if any
+    exception_docs = state.property_docs.get('.'.join(entry.path), {}).get('raise')
+    if exception_docs:
+        out.exceptions = []
+        for type_, content in exception_docs:
+            exception = Empty()
+            exception.type = type_
+            exception.type_link = make_name_link(state, entry.path, type_)
+            exception.content = render_inline_rst(state, content)
+            out.exceptions += [exception]
+        out.has_details = True
+
     if not state.config['SEARCH_DISABLED']:
         result = Empty()
         result.flags = ResultFlag.from_type(ResultFlag.NONE, EntryType.PROPERTY)
index 1bce6420a5224d8fbc8abaf2b058581b1cdc6159..25440508bb901b5f27f554da64d48ff3c3d043c6 100644 (file)
@@ -6,7 +6,7 @@
             {% if function.summary %}
             <p>{{ function.summary }}</p>
             {% endif %}
-            {% if function.has_param_details or function.return_value %}
+            {% if function.has_param_details or function.exceptions or function.return_value %}
             <table class="m-table m-fullwidth m-flat">
               {% if function.has_param_details %}
               <thead>
                 {% endfor %}
               </tbody>
               {% endif %}
+              {% if function.exceptions %}
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                {% for exception in function.exceptions %}
+                <tr>
+                  <td{% if loop.index == 1 and not function.has_param_details %} style="width: 1%"{% endif %}>{{ exception.type_link }}</td>
+                  <td>{{ exception.content }}</td>
+                </tr>
+                {% endfor %}
+              </tbody>
+              {% endif %}
               {% if function.return_value %}
               <tfoot>
                 <tr>
-                  <th{% if not function.has_param_details %} style="width: 1%"{% endif %}>Returns</th>
+                  <th{% if not function.has_param_details and not function.exceptions %} style="width: 1%"{% endif %}>Returns</th>
                   <td>{{ function.return_value }}</td>
                 </tr>
               </tfoot>
index 0b72f950ce927acfd4bc445d02f652a5c60e4462..1aa22e5a0fdf5ec9de9ab928e46a088fb2967899 100644 (file)
@@ -5,6 +5,21 @@
             {% if property.summary %}
             <p>{{ property.summary }}</p>
             {% endif %}
+            {% if property.exceptions %}
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                {% for exception in property.exceptions %}
+                <tr>
+                  <td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ exception.type_link }}</td>
+                  <td>{{ exception.content }}</td>
+                </tr>
+                {% endfor %}
+              </tbody>
+            </table>
+            {% endif %}
             {% if property.content %}
 {{ property.content }}
             {% endif %}
index 81d2eff44bd2f4393a9878b26dd5dcc1974edf6d..a723fbf371343851682de55241d616e4d891c0b6 100644 (file)
@@ -71,6 +71,10 @@ add any detailed block.</dd>
               <span class="m-doc-wrap-bumper">def <a href="#method_param_docs" class="m-doc">method_param_docs</a>(</span><span class="m-doc-wrap">self, a, b)</span>
             </dt>
             <dd>This method gets its params except self documented</dd>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#method_param_exception_return_docs" class="m-doc">method_param_exception_return_docs</a>(</span><span class="m-doc-wrap">self, a, b)</span>
+            </dt>
+            <dd>This one documents params and raised exceptions</dd>
             <dt>
               <span class="m-doc-wrap-bumper">def <a href="#method_with_details" class="m-doc">method_with_details</a>(</span><span class="m-doc-wrap">self)</span>
             </dt>
@@ -102,6 +106,11 @@ add any detailed block.</dd>
               <a href="#annotated_property" class="m-doc">annotated_property</a>: float <span class="m-label m-flat m-warning">get</span>
             </dt>
             <dd>This is an annotated property</dd>
+            <dt>
+              <a href="#property_exception_docs" class="m-doc">property_exception_docs</a> <span class="m-label m-flat m-warning">get</span>
+            </dt>
+            <dd>This one documents raised exceptions in an (otherwise unneeded)
+detail view</dd>
           </dl>
         </section>
         <section id="data">
@@ -151,6 +160,42 @@ add any detailed block.</dd>
             </table>
 <p>The <code>self</code> isn't documented and thus also not included in the list.</p>
           </div></section>
+          <section class="m-doc-details" id="method_param_exception_return_docs"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#method_param_exception_return_docs" class="m-doc-self">method_param_exception_return_docs</a>(</span><span class="m-doc-wrap">self, a, b)</span></span>
+            </h3>
+            <p>This one documents params and raised exceptions</p>
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Parameters</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td style="width: 1%">a</td>
+                  <td>The first parameter</td>
+                </tr>
+                <tr>
+                  <td>b</td>
+                  <td>The second parameter</td>
+                </tr>
+              </tbody>
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td>AttributeError</td>
+                  <td>If you do bad things to it</td>
+                </tr>
+              </tbody>
+              <tfoot>
+                <tr>
+                  <th>Returns</th>
+                  <td>If you don't do bad things to it</td>
+                </tr>
+              </tfoot>
+            </table>
+          </div></section>
           <section class="m-doc-details" id="method_with_details"><div>
             <h3>
               <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#method_with_details" class="m-doc-self">method_with_details</a>(</span><span class="m-doc-wrap">self)</span></span>
@@ -181,6 +226,24 @@ add any detailed block.</dd>
             <p>This is an annotated property</p>
 <p>Annotated property, using summary from the docstring.</p>
           </div></section>
+          <section class="m-doc-details" id="property_exception_docs"><div>
+            <h3>
+              content.<wbr />Class.<wbr /><a href="#property_exception_docs" class="m-doc-self">property_exception_docs</a> <span class="m-label m-flat m-warning">get</span>
+            </h3>
+            <p>This one documents raised exceptions in an (otherwise unneeded)
+detail view</p>
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td style="width: 1%">AttributeError</td>
+                  <td>If you do bad things to it</td>
+                </tr>
+              </tbody>
+            </table>
+          </div></section>
         </section>
         <section>
           <h2>Data documentation</h2>
index b21bebd87b82a4f4832285c37482990751470944..3bc425082eff5dd836b67047839725e887ef32a4 100644 (file)
@@ -84,6 +84,11 @@ add any detailed block.</dd>
         <section id="functions">
           <h2><a href="#functions">Functions</a></h2>
           <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#exception_docs" class="m-doc">exception_docs</a>(</span><span class="m-doc-wrap">)</span>
+            </dt>
+            <dd>This one documents raised exceptions in an (otherwise unneeded) detail
+view</dd>
             <dt id="foo">
               <span class="m-doc-wrap-bumper">def <a href="#foo" class="m-doc-self">foo</a>(</span><span class="m-doc-wrap">a, b)</span>
             </dt>
@@ -180,6 +185,32 @@ directive...</p>
         </section>
         <section>
           <h2>Function documentation</h2>
+          <section class="m-doc-details" id="exception_docs"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#exception_docs" class="m-doc-self">exception_docs</a>(</span><span class="m-doc-wrap">)</span></span>
+            </h3>
+            <p>This one documents raised exceptions in an (otherwise unneeded) detail
+view</p>
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td style="width: 1%">ValueError</td>
+                  <td>This thing fires</td>
+                </tr>
+                <tr>
+                  <td>ValueError</td>
+                  <td>This <em>same</em> thing fires <em>also</em> for this reason</td>
+                </tr>
+                <tr>
+                  <td>RuntimeError</td>
+                  <td>This another thing fires too</td>
+                </tr>
+              </tbody>
+            </table>
+          </div></section>
           <section class="m-doc-details" id="foo_with_details"><div>
             <h3>
               <span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#foo_with_details" class="m-doc-self">foo_with_details</a>(</span><span class="m-doc-wrap">a, b)</span></span>
index ace7afb68fd582fea524f838b2297d5ccb682321..e555cf0f7c8e16229c069a362b4c07580b45b903 100644 (file)
@@ -26,6 +26,9 @@ class Class:
     def method_param_docs(self, a, b):
         """This method gets its params except self documented"""
 
+    def method_param_exception_return_docs(self, a, b):
+        """This one documents params and raised exceptions"""
+
     @property
     def a_property(self):
         """This summary is not shown either"""
@@ -38,6 +41,11 @@ class Class:
     def annotated_property(self) -> float:
         """This is an annotated property"""
 
+    @property
+    def property_exception_docs(self):
+        """This one documents raised exceptions in an (otherwise unneeded)
+        detail view"""
+
     DATA_WITH_DETAILS: str = 'this blows'
 
 class ClassWithSummary:
@@ -102,6 +110,11 @@ def full_docstring(a, b) -> str:
     Like this.
     """
 
+def exception_docs():
+    """This one documents raised exceptions in an (otherwise unneeded) detail
+    view
+    """
+
 # This should check we handle reST parsing errors in external docs gracefully.
 # Will probably look extra weird in the output tho, but that's okay -- it's an
 # error after all.
index fd02d9356d299d678377ee1bdeee58560ac5df83..21065c3f9846313120f5bc43b6e1259a25f6dfa8 100644 (file)
 
     The ``self`` isn't documented and thus also not included in the list.
 
+.. py:function:: content.Class.method_param_exception_return_docs
+    :param a: The first parameter
+    :param b: The second parameter
+    :raise AttributeError: If you do bad things to it
+    :return: If you don't do bad things to it
+
 .. py:property:: content.Class.a_property
     :summary: This overwrites the docstring for :ref:`a_property`, but doesn't
         add any detailed block.
@@ -72,6 +78,9 @@
 
     Annotated property, using summary from the docstring.
 
+.. py:property:: content.Class.property_exception_docs
+    :raise AttributeError: If you do bad things to it
+
 .. py:data:: content.Class.DATA_WITH_DETAILS
 
     Detailed docs for :ref:`DATA_WITH_DETAILS` in a class to check
@@ -136,6 +145,11 @@ Doing :p:`this` here is not good either.
     :param a: First parameter
     :param b: Second
 
+.. py:function:: content.exception_docs
+    :raise ValueError: This thing fires
+    :raise ValueError: This *same* thing fires *also* for this reason
+    :raise RuntimeError: This another thing fires too
+
 .. py:data:: content.CONSTANT
     :summary: This is finally a docstring for :ref:`CONSTANT`
 
index 4548d45b882d1938ba5554b860308dcf34673d76..015f93e69e1d3d0d6345bec70c25f02acf478d85 100644 (file)
@@ -7,6 +7,14 @@
     function we need to say :ref:`inspect_type_links.open()`. If it would be
     the other way around, there would be no simple way to link to builtins.
 
+.. py:function:: inspect_type_links.open
+    :raise ValueError: If this is not a can, crosslinking to :ref:`ValueError`
+        of course.
+
+.. py:property:: inspect_type_links.Foo.prop
+    :raise SystemError: If you look at it wrong, crosslinking to
+        :ref:`SystemError` of course.
+
 .. py:module:: inspect_type_links.first
 
     :ref:`Foo`, :ref:`first.Foo` and :ref:`inspect_type_links.first.Foo` should
diff --git a/documentation/test_python/inspect_type_links/inspect_type_links.Foo.html b/documentation/test_python/inspect_type_links/inspect_type_links.Foo.html
new file mode 100644 (file)
index 0000000..a1f9047
--- /dev/null
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>inspect_type_links.Foo | My Python Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          <span class="m-breadcrumb"><a href="inspect_type_links.html">inspect_type_links</a>.<wbr/></span>Foo <span class="m-thin">class</span>
+        </h1>
+        <p>A class in the root module</p>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#properties">Properties</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+        <section id="properties">
+          <h2><a href="#properties">Properties</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <a href="#prop" class="m-doc">prop</a> <span class="m-label m-flat m-warning">get</span>
+            </dt>
+            <dd>Here just to test the raise option</dd>
+          </dl>
+        </section>
+        <section>
+          <h2>Property documentation</h2>
+          <section class="m-doc-details" id="prop"><div>
+            <h3>
+              inspect_type_links.<wbr />Foo.<wbr /><a href="#prop" class="m-doc-self">prop</a> <span class="m-label m-flat m-warning">get</span>
+            </h3>
+            <p>Here just to test the raise option</p>
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td style="width: 1%"><a href="https://docs.python.org/3/library/exceptions.html#SystemError" class="m-doc-external">SystemError</a></td>
+                  <td>If you look at it wrong, crosslinking to
+<a class="m-doc-external" href="https://docs.python.org/3/library/exceptions.html#SystemError">SystemError</a> of course.</td>
+                </tr>
+              </tbody>
+            </table>
+          </div></section>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
index 16df2d11e49b758e0bad3d3a881c57477dc24f0b..994ac451f287eb63ed17ffb054889221f3b482e4 100644 (file)
@@ -61,12 +61,33 @@ the other way around, there would be no simple way to link to builtins.</p>
         <section id="functions">
           <h2><a href="#functions">Functions</a></h2>
           <dl class="m-doc">
-            <dt id="open">
-              <span class="m-doc-wrap-bumper">def <a href="#open" class="m-doc-self">open</a>(</span><span class="m-doc-wrap">)</span>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#open" class="m-doc">open</a>(</span><span class="m-doc-wrap">)</span>
             </dt>
             <dd>A function that opens cans.</dd>
           </dl>
         </section>
+        <section>
+          <h2>Function documentation</h2>
+          <section class="m-doc-details" id="open"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def inspect_type_links.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#open" class="m-doc-self">open</a>(</span><span class="m-doc-wrap">)</span></span>
+            </h3>
+            <p>A function that opens cans.</p>
+            <table class="m-table m-fullwidth m-flat">
+              <thead>
+                <tr><th colspan="2">Exceptions</th></tr>
+              </thead>
+              <tbody>
+                <tr>
+                  <td style="width: 1%"><a href="https://docs.python.org/3/library/exceptions.html#ValueError" class="m-doc-external">ValueError</a></td>
+                  <td>If this is not a can, crosslinking to <a class="m-doc-external" href="https://docs.python.org/3/library/exceptions.html#ValueError">ValueError</a>
+of course.</td>
+                </tr>
+              </tbody>
+            </table>
+          </div></section>
+        </section>
       </div>
     </div>
   </div>
index b248126e22d75b181650bb28b384b689e46ba1a7..590c8b9a458b39353403a1dc01d8472881f47fef 100644 (file)
@@ -3,6 +3,10 @@ from . import first, second
 class Foo:
     """A class in the root module"""
 
+    @property
+    def prop(self):
+        """Here just to test the raise option"""
+
 class Bar:
     """Another class in the root module"""
 
index 4ba365f99ad1a6f848ae5f4ed7c9986f69ebd087..57599dba6afa2c6698ff6f041c7a90c78dd91776 100644 (file)
@@ -175,6 +175,7 @@ class TypeLinks(BaseInspectTestCase):
         self.assertEqual(*self.actual_expected_contents('index.html'))
 
         self.assertEqual(*self.actual_expected_contents('inspect_type_links.html'))
+        self.assertEqual(*self.actual_expected_contents('inspect_type_links.Foo.html'))
         self.assertEqual(*self.actual_expected_contents('inspect_type_links.first.html'))
         self.assertEqual(*self.actual_expected_contents('inspect_type_links.first.Foo.html'))
         self.assertEqual(*self.actual_expected_contents('inspect_type_links.first.Foo.Foo.html'))
index 3446581e4fcb6a47d9f822b8203d0e774402f5aa..ba7db45e32605d850de0b3f4d9a4582096c1606b 100755 (executable)
@@ -173,6 +173,7 @@ class PyFunction(rst.Directive):
     required_arguments = 1
     option_spec = {'summary': directives.unchanged,
                    'param': directives_unchanged_list,
+                   'raise': directives_unchanged_list,
                    'return': directives.unchanged}
 
     def run(self):
@@ -182,12 +183,21 @@ class PyFunction(rst.Directive):
         for name, content in self.options.get('param', []):
             if name in params: raise KeyError("duplicate param {}".format(name))
             params[name] = content
+        # Check that exceptions are parsed properly. This will blow up if the
+        # exception name is not specified. Unlike function params not turning
+        # these into a dict since a single type can be raised for multiple
+        # reasons.
+        raises = []
+        for name, content in self.options.get('raise', []):
+            raises += [(name, content)]
 
         output = function_doc_output.setdefault(self.arguments[0], {})
         if self.options.get('summary'):
             output['summary'] = self.options['summary']
         if params:
             output['params'] = params
+        if raises:
+            output['raise'] = raises
         if self.options.get('return'):
             output['return'] = self.options['return']
         if self.content:
@@ -198,10 +208,21 @@ class PyProperty(rst.Directive):
     final_argument_whitespace = True
     has_content = True
     required_arguments = 1
-    option_spec = {'summary': directives.unchanged}
+    option_spec = {'summary': directives.unchanged,
+                   'raise': directives_unchanged_list}
 
     def run(self):
+        # Check that exceptions are parsed properly. This will blow up if the
+        # exception name is not specified. Unlike function params not turning
+        # these into a dict since a single type can be raised for multiple
+        # reasons.
+        raises = []
+        for name, content in self.options.get('raise', []):
+            raises += [(name, content)]
+
         output = property_doc_output.setdefault(self.arguments[0], {})
+        if raises:
+            output['raise'] = raises
         if self.options.get('summary'):
             output['summary'] = self.options['summary']
         if self.content:
@@ -539,6 +560,8 @@ def merge_inventories(name_map, **kwargs):
         # TODO: this will blow up if the above loop is never entered (which is
         # unlikely) as EntryType is defined there
         (EntryType.CLASS, 'py:class'),
+        # Otherwise we can't link to standard exceptions from :raise:
+        (EntryType.CLASS, 'py:exception'), # TODO: special type for these?
         (EntryType.DATA, 'py:data'), # typing.Tuple or typing.Any is data
         # Those are custom to m.css, not in Sphinx
         (EntryType.ENUM, 'py:enum'),