Skip to content

Conversation

@khushiiagrawal
Copy link

@khushiiagrawal khushiiagrawal commented Jan 1, 2026

Summary

Fixes two issues with inheritance documentation:

  1. Uses Sphinx RST syntax :py:class:BaseClass`` instead of plain text for inheritance links
  2. Groups inherited methods separately and adds "Inherited from :py:class:BaseClass." notation

Fixes #180

Changes

  • CodeGenerator.py: Generate Sphinx RST syntax in class docstrings; group inherited methods after class-defined methods
  • PXDParser.py: Track inherited methods with base class names
  • DeclResolver.py: Pass base class name when attaching inherited methods
  • test_full_library.py: Update expected docstring length (93→101 chars)

Testing

  • All existing tests pass
  • Verified Sphinx RST syntax appears in class docstrings
  • Verified inherited methods are grouped after class-defined methods
  • Verified "Inherited from" notation appears in .pyi file docstrings

Test case: Bklass inheriting from A_second - inherits callA2() method

Summary by CodeRabbit

  • New Features

    • Class docstrings now show Sphinx-style "Inherited from" links and inherited-source annotations.
    • Added a script to build a self-contained Sphinx demo illustrating inheritance documentation.
  • Improvements

    • Methods listed by origin (class-defined first, inherited after); constructors preserve original order.
    • Inheritance context propagates into runtime wrappers and type stubs.
    • getitem bounds checks now apply only for integral index types.
  • Tests

    • Updated test expectation for docstring formatting.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 1, 2026

📝 Walkthrough

Walkthrough

Class inheritance metadata is tracked from parsing through resolution into code generation: base-class names are recorded when attaching inherited methods, propagated as inherited_from through wrapper and stub creation, and rendered in class docstrings and method/type stubs. A new demo script automates generating a Sphinx demo for these inheritance docs.

Changes

Cohort / File(s) Change summary
Code generation
autowrap/CodeGenerator.py
Thread inherited_from through wrapper creation APIs; generate Sphinx-style :py:class: links in class docstrings; separate class-defined vs inherited methods (preserve ctor order); annotate runtime wrappers and typestubs with inherited-from info; add _is_integral_type and adjust getitem/setitem bounds handling.
Declaration resolution
autowrap/DeclResolver.py
Pass base class name when attaching inherited methods so inherited origin is available to consumers (calls attach_base_methods(..., base_class_name)).
PXD parsing / AST
autowrap/PXDParser.py
Add inherited_method_bases to CppClassDecl; accept optional base_class_name in attach_base_methods and record the originating base for each attached method.
Tests
tests/test_full_library.py
Update expected docstring length to account for Sphinx :py:class: RST markup (93101) with explanatory comment.
Tooling / Demo
create_sphinx_demo.py
New script to build an end-to-end Sphinx demo: runs autowrap, creates setup.py, builds extension, configures Sphinx, generates index.rst, builds HTML and attempts to open it. Adds run_command and main.

Sequence Diagram(s)

sequenceDiagram
  %% Styling note: changed/added interactions are highlighted with green notes
  autonumber
  participant P as PXDParser
  participant R as DeclResolver
  participant C as CodeGenerator
  participant FS as Files/Tests

  Note over P,R: Parse .pxd and produce CppClassDecls
  P->>R: attach_base_methods(transformed_methods, base_class_name)
  Note right of R: R records base name per method\nand stores in class declaration
  R->>C: pass resolved class decls (with inherited_method_bases)
  Note left of C: CodeGenerator receives inherited context\nand threads `inherited_from` through APIs
  C->>C: create wrappers & typestubs\n(include `inherited_from` annotations)
  C->>FS: write updated runtime wrappers & .pyi stubs
  FS->>User: tests assert docstring lengths and docs reflect\n:py:class: links for inherited classes
  Note over P,C: (new) Sphinx demo script automates these steps
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I'm a rabbit with a quill and a hop,
I stitched base names from bottom to top.
Docs now link where methods come from,
Stubs wear badges that read "inherited_from."
🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the two main changes: Sphinx RST syntax for inheritance documentation and method grouping by inheritance status.
Linked Issues check ✅ Passed All three coding requirements from issue #180 are met: Sphinx :py:class: syntax is implemented, inherited methods are grouped separately, and 'Inherited from' annotations are added to docstrings.
Out of Scope Changes check ✅ Passed All changes are scoped to inheritance documentation and method grouping. The new create_sphinx_demo.py script is a demonstration utility supporting verification of the changes.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@khushiiagrawal
Copy link
Author

@jpfeuffer Please take a look.
Thanks!

@jpfeuffer
Copy link
Contributor

Looks good but to be sure, we would need to see a sphinx documentation page with autodoc for this.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
create_sphinx_demo.py (4)

50-53: Consider using unpacking for more idiomatic list construction.

The list concatenation works correctly, but using the unpacking operator * is more idiomatic in modern Python.

🔎 Proposed refactor
     run_command([
         "autowrap",
-        "--out", "moduleB.pyx"
-    ] + pxd_files)
+        "--out", "moduleB.pyx",
+        *pxd_files
+    ])

122-139: Simplify the conf.py modification logic.

The logic to insert sys.path.insert has several issues:

  1. Redundant check at line 130 (already checked at line 122)
  2. The variable new_lines may not be defined if the condition at line 129 never matches
  3. The isinstance(new_lines, list) check at line 139 is unnecessary since new_lines is always a list when defined
🔎 Proposed refactor for clearer logic
         # Add path configuration if not already present
         if "sys.path.insert" not in conf_content:
-            # Find the extensions line and add after it
-            lines = conf_content.split("\n")
-            new_lines = []
-            for i, line in enumerate(lines):
-                new_lines.append(line)
-                # Add after extensions or after imports
-                if "extensions = [" in line or (i > 0 and "import sys" in lines[i-1] and "import os" in line):
-                    if "sys.path.insert" not in conf_content:
-                        new_lines.append("")
-                        new_lines.append("# Add current directory to path for autodoc")
-                        new_lines.append("import sys")
-                        new_lines.append("import os")
-                        new_lines.append("sys.path.insert(0, os.path.abspath('.'))")
-                        break
-        
-        with open(conf_py_path, "w") as f:
-            f.write("\n".join(new_lines) if isinstance(new_lines, list) else conf_content)
+            # Prepend the path configuration at the top
+            path_setup = """import sys
+import os
+
+# Add current directory to path for autodoc
+sys.path.insert(0, os.path.abspath('.'))
+
+"""
+            conf_content = path_setup + conf_content
+        
+        with open(conf_py_path, "w") as f:
+            f.write(conf_content)

226-231: Remove unnecessary f-string prefixes.

Lines 226 and 228 use f-strings without any placeholders. Regular strings would be clearer.

🔎 Proposed refactor
-    print(f"\nHTML documentation is available at:")
+    print("\nHTML documentation is available at:")
     print(f"  {html_path}")
-    print(f"\nTo view it, run:")
+    print("\nTo view it, run:")
     print(f"  open {html_path}  # macOS")

234-240: Consider catching more specific exceptions.

Catching bare Exception is overly broad. For better error handling, catch specific exceptions that webbrowser.open() might raise, such as OSError or webbrowser.Error.

🔎 Proposed refactor
     # Try to open automatically
     try:
         import webbrowser
         webbrowser.open(f"file://{html_path}")
         print("\nOpened in default browser!")
-    except Exception as e:
+    except (OSError, webbrowser.Error) as e:
         print(f"\nCould not open browser automatically: {e}")
         print("Please open the file manually.")
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d492ada and 8b28f26.

📒 Files selected for processing (1)
  • create_sphinx_demo.py
🧰 Additional context used
🧬 Code graph analysis (1)
create_sphinx_demo.py (1)
tests/test_files/templated.hpp (1)
  • f (61-64)
🪛 Ruff (0.14.10)
create_sphinx_demo.py

13-13: subprocess call: check for execution of untrusted input

(S603)


50-53: Consider ["autowrap", "--out", "moduleB.pyx", *pxd_files] instead of concatenation

Replace with ["autowrap", "--out", "moduleB.pyx", *pxd_files]

(RUF005)


104-104: Local variable quickstart_input is assigned to but never used

Remove assignment to unused variable quickstart_input

(F841)


226-226: f-string without any placeholders

Remove extraneous f prefix

(F541)


228-228: f-string without any placeholders

Remove extraneous f prefix

(F541)


238-238: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (1)
create_sphinx_demo.py (1)

10-25: No security concern—all run_command calls already use list-based commands.

All four call sites of run_command (lines 50, 88, 105, and 219) pass list-based commands, which means shell=False is always used. The hard-coded commands reference only known tools (autowrap, sphinx-quickstart, sphinx-build, python) and project paths, with no user input interpolation. This script follows the recommended best practice and has no security risk.

@khushiiagrawal
Copy link
Author

khushiiagrawal commented Jan 1, 2026

Looks good but to be sure, we would need to see a sphinx documentation page with autodoc for this.

@jpfeuffer I've generated Sphinx documentation demonstrating the inheritance fixes. Here's what the screenshot shows:

Screenshot 2026-01-01 at 11 37 31 PM Screenshot 2026-01-01 at 11 37 45 PM

Inheritance link in class docstring: "— Inherits from A_second" - A_second is a clickable cross-reference
Inherited method notation: "Inherited from A_second." - A_second is a clickable link
Method grouping: Class-defined methods appear first, inherited methods after

The HTML file is attached, and you can regenerate it using python create_sphinx_demo.py (script included in this PR). The Sphinx RST syntax works correctly and creates functional cross-references.

@jpfeuffer
Copy link
Contributor

jpfeuffer commented Jan 1, 2026

Looks pretty good. Can you demonstrate the method grouping. I only see one method.
Also, there seems to be a broken link in your second picture.
I wonder if we should put inherited methods first or last?

@khushiiagrawal
Copy link
Author

@jpfeuffer Thank you for the feedback! I've investigated and fixed the issues:

1. Method Grouping

I found and fixed a bug in CodeGenerator.py where only the last inherited method was being generated due to an indentation error. Now all inherited methods are properly generated and grouped.

The Bklass class now clearly demonstrates method grouping with:

  • 4 class-defined methods: i_, __init__() (2 overloads), classMethod1(), classMethod2()
  • 4 inherited methods: anotherMethod(), callA2(), getValue(), methodA() (all from A_second)

2. Broken Link

I checked all links in the generated HTML. All :py:class:A_second`` references correctly resolve to the A_second class definition. The links are working properly now that all methods are generated.

3. Inherited Methods Order

Currently, inherited methods appear after class-defined methods (following Python documentation conventions). I'm happy to change this to put inherited methods first if you prefer. What would you like?

Screenshot 2026-01-02 at 11 07 01 AM
Screen.Recording.2026-01-02.at.11.07.10.AM.mov

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
create_sphinx_demo.py (2)

97-110: Remove unused sphinx_config and quickstart_input variables.

The past review comment correctly identifies this issue: these variables are defined but never used since sphinx-quickstart is invoked with non-interactive command-line flags.


29-29: Incorrect project root calculation will break the script.

The past review comment correctly identifies this issue: Path(__file__).parent resolves to src/scripts/, not the project root. This will cause failures when accessing test files at lines 46-48.

🧹 Nitpick comments (3)
create_sphinx_demo.py (3)

7-7: Remove unused import.

The tempfile module is imported but never used in the script.

🔎 Proposed fix
 import os
 import sys
 import shutil
 import subprocess
-import tempfile
 from pathlib import Path

51-54: Consider using unpacking for cleaner list construction.

The list concatenation can be simplified using the unpacking operator.

🔎 Proposed refactor
-    run_command([
-        "autowrap",
-        "--out", "moduleB.pyx"
-    ] + pxd_files)
+    run_command([
+        "autowrap",
+        "--out", "moduleB.pyx",
+        *pxd_files
+    ])

228-233: Remove unnecessary f-string prefixes.

Lines 228 and 230 use f-string prefixes but contain no placeholders.

🔎 Proposed refactor
-    print(f"\nHTML documentation is available at:")
+    print("\nHTML documentation is available at:")
     print(f"  {html_path}")
-    print(f"\nTo view it, run:")
+    print("\nTo view it, run:")
     print(f"  open {html_path}  # macOS")
     print(f"  xdg-open {html_path}  # Linux")
     print(f"  start {html_path}  # Windows")
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bacbe9 and 844429d.

📒 Files selected for processing (1)
  • create_sphinx_demo.py
🧰 Additional context used
🪛 Ruff (0.14.10)
create_sphinx_demo.py

13-13: subprocess call: check for execution of untrusted input

(S603)


51-54: Consider ["autowrap", "--out", "moduleB.pyx", *pxd_files] instead of concatenation

Replace with ["autowrap", "--out", "moduleB.pyx", *pxd_files]

(RUF005)


106-106: Local variable quickstart_input is assigned to but never used

Remove assignment to unused variable quickstart_input

(F841)


228-228: f-string without any placeholders

Remove extraneous f prefix

(F541)


230-230: f-string without any placeholders

Remove extraneous f prefix

(F541)


240-240: Do not catch blind exception: Exception

(BLE001)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adapt docstring generation regarding inheritance

2 participants