chiark / gitweb /
documentation/python: more graceful handling of duplicate classes.
authorVladimír Vondruš <mosra@centrum.cz>
Thu, 7 May 2020 21:42:05 +0000 (23:42 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 7 May 2020 21:43:42 +0000 (23:43 +0200)
Not ideal (as the resulting docs are confusing), but at least something.
Creating two copies of the same class could easily lead to infinite
recursion in code paths that don't expect it, so not doing that.

documentation/python.py
documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.Bar.html [new file with mode: 0644]
documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.html [new file with mode: 0644]
documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.sub.html [new file with mode: 0644]
documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/__init__.py [new file with mode: 0644]
documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/sub.py [new file with mode: 0644]
documentation/test_python/test_inspect.py

index 933a4106077759027e64553a01ec2883b675be77..08ebcc83772090966568c32aaec9c10da01c6894 100755 (executable)
@@ -377,10 +377,18 @@ def crawl_enum(state: State, path: List[str], enum_, parent_url):
 def crawl_class(state: State, path: List[str], class_):
     assert inspect.isclass(class_)
 
-    # TODO: if this fires, it means there's a class duplicated in more than one
-    # __all__ (or it gets picked up implicitly and then in __all__) -- how to
-    # handle gracefully?
-    assert id(class_) not in state.crawled
+    # If this fires, it means there's a class duplicated in more than one
+    # __all__ (or it gets picked up implicitly and then in __all__). It usually
+    # means there's a mess in imports, unfortunately this is more common than
+    # one would hope so we can't just assert.
+    if id(class_) in state.crawled:
+        for name, previous_entry in state.name_map.items():
+            if previous_entry.object == class_: break
+        else: assert False
+        logging.error("Class %s previously found in %s, only one occurence will be chosen. Ensure each class is exposed only in a single module for generating correct documentation.", '.'.join(path), '.'.join(previous_entry.path))
+        state.name_map['.'.join(path)] = previous_entry
+        return
+
     state.crawled.add(id(class_))
 
     class_entry = Empty()
diff --git a/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.Bar.html b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.Bar.html
new file mode 100644 (file)
index 0000000..8b9fbae
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>inspect_duplicate_class.Bar | 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_duplicate_class.html">inspect_duplicate_class</a>.<wbr/></span>Bar <span class="m-thin">class</span>
+        </h1>
+        <p>A class present in two modules.</p>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
diff --git a/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.html b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.html
new file mode 100644 (file)
index 0000000..18d4039
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>inspect_duplicate_class | 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>
+          inspect_duplicate_class <span class="m-thin">module</span>
+        </h1>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#packages">Modules</a></li>
+                <li><a href="#classes">Classes</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+        <section id="namespaces">
+          <h2><a href="#namespaces">Modules</a></h2>
+          <dl class="m-doc">
+            <dt>module <a href="inspect_duplicate_class.sub.html" class="m-doc">sub</a></dt>
+            <dd>A submodule.</dd>
+          </dl>
+        </section>
+        <section id="classes">
+          <h2><a href="#classes">Classes</a></h2>
+          <dl class="m-doc">
+            <dt>class <a href="inspect_duplicate_class.Bar.html" class="m-doc">Bar</a></dt>
+            <dd>A class present in two modules.</dd>
+          </dl>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
diff --git a/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.sub.html b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.sub.html
new file mode 100644 (file)
index 0000000..627b3de
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>inspect_duplicate_class.sub | 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_duplicate_class.html">inspect_duplicate_class</a>.<wbr/></span>sub <span class="m-thin">module</span>
+        </h1>
+        <p>A submodule.</p>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#classes">Classes</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+        <section id="classes">
+          <h2><a href="#classes">Classes</a></h2>
+          <dl class="m-doc">
+            <dt>class <a href="inspect_duplicate_class.Bar.html" class="m-doc">Bar</a></dt>
+            <dd>A class present in two modules.</dd>
+          </dl>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
diff --git a/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/__init__.py b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/__init__.py
new file mode 100644 (file)
index 0000000..4bea836
--- /dev/null
@@ -0,0 +1,4 @@
+
+from .sub import Foo as Bar
+
+__all__ = ['sub', 'Bar']
diff --git a/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/sub.py b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/sub.py
new file mode 100644 (file)
index 0000000..3388ace
--- /dev/null
@@ -0,0 +1,5 @@
+"""A submodule."""
+
+class Foo:
+    """A class present in two modules."""
+    pass
index 7ecf427856986c3179b2828acf128f3d1af9373f..4fed48f324d0f0f64d90c5ae35e099ccfbeef995 100644 (file)
@@ -257,3 +257,10 @@ class ValueFormatting(BaseInspectTestCase):
     def test(self):
         self.run_python({})
         self.assertEqual(*self.actual_expected_contents('inspect_value_formatting.html'))
+
+class DuplicateClass(BaseInspectTestCase):
+    def test(self):
+        self.run_python({})
+        self.assertEqual(*self.actual_expected_contents('inspect_duplicate_class.html'))
+        self.assertEqual(*self.actual_expected_contents('inspect_duplicate_class.sub.html'))
+        self.assertEqual(*self.actual_expected_contents('inspect_duplicate_class.Bar.html'))