From f45a8b060c5cd875da1fe66982025627c2747359 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 7 May 2020 23:42:05 +0200 Subject: [PATCH] documentation/python: more graceful handling of duplicate classes. 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 | 16 ++++-- .../inspect_duplicate_class.Bar.html | 31 ++++++++++ .../inspect_duplicate_class.html | 56 +++++++++++++++++++ .../inspect_duplicate_class.sub.html | 49 ++++++++++++++++ .../inspect_duplicate_class/__init__.py | 4 ++ .../inspect_duplicate_class/sub.py | 5 ++ documentation/test_python/test_inspect.py | 7 +++ 7 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.Bar.html create mode 100644 documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.html create mode 100644 documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.sub.html create mode 100644 documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/__init__.py create mode 100644 documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/sub.py diff --git a/documentation/python.py b/documentation/python.py index 933a4106..08ebcc83 100755 --- a/documentation/python.py +++ b/documentation/python.py @@ -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 index 00000000..8b9fbae5 --- /dev/null +++ b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.Bar.html @@ -0,0 +1,31 @@ + + + + + inspect_duplicate_class.Bar | My Python Project + + + + + +
+
+ + 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 index 00000000..18d4039f --- /dev/null +++ b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.html @@ -0,0 +1,56 @@ + + + + + inspect_duplicate_class | My Python Project + + + + + +
+
+
+
+
+

+ inspect_duplicate_class module +

+
+

Contents

+ +
+
+

Modules

+
+
module sub
+
A submodule.
+
+
+
+

Classes

+
+
class Bar
+
A class present in two modules.
+
+
+
+
+
+
+ + 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 index 00000000..627b3de9 --- /dev/null +++ b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class.sub.html @@ -0,0 +1,49 @@ + + + + + inspect_duplicate_class.sub | My Python Project + + + + + +
+
+ + 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 index 00000000..4bea8361 --- /dev/null +++ b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/__init__.py @@ -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 index 00000000..3388acee --- /dev/null +++ b/documentation/test_python/inspect_duplicate_class/inspect_duplicate_class/sub.py @@ -0,0 +1,5 @@ +"""A submodule.""" + +class Foo: + """A class present in two modules.""" + pass diff --git a/documentation/test_python/test_inspect.py b/documentation/test_python/test_inspect.py index 7ecf4278..4fed48f3 100644 --- a/documentation/test_python/test_inspect.py +++ b/documentation/test_python/test_inspect.py @@ -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')) -- 2.30.2