sphinx-doc / sphinx-doc/sphinx

Inheritance diagram can't import classes within a class

Open
#5,518 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

extensions:graphviz
Dominant language
Python
Stars
8k
Forks
2.6k
PR merge metrics
No merged PRs in 30d

Description

Apologies if this is a duplicated issue, I searched but nothing turned up.

Problem

The inheritance diagram plugin fails to import classes within a class within a package/module.

The root-cause of the problem is the function try_import() within inheritance_diagram.py. The current implementation tries to import the full object name, and if that fails, it attempts to strip off the class/final module name, and import what was before that. This works for cases like foo_package.foo_subpackage.FooClass, but not foo_package.FooClass.BarClass.

Procedure to reproduce the problem
  1. Create a new python package called foo_package, add __init__.py, doc folder, etc..
  2. Create a new sphinx project for foo_package under /doc/
  3. In conf.py, set it up to point to the new package, and add sphinx.ext.inheritance_diagram to the extensions list.
  4. Add the code below into foo_package and index.rst:

__init__.py within foo_package:

class Foo:
    class Bar:
        pass
    class Baz(Bar):
        pass

index.rst for foo_package:

.. inheritance-diagram::
    foo_package.Foo.Bar
    foo_package.Foo.Baz
Error logs / results
index.rst:16: WARNING: Could not import class or module 'foo_package.Foo.Bar' specified for inheritance diagram

The function try_import() is splitting the object name into foo_package.Foo and trying to import that. Which doesn't work because it's a class.

Expected results

The inheritance diagram plugin should be able to recognize foo_package.Foo.Bar as a class 'Bar' inside class Foo, inside module foo_package.

Environment info
  • OS: Windows 7
  • Python version: 3.5.1
  • Sphinx version: 1.8.1 (with a fix applied that prevented the released version from running with 3.5.1)
Proposed solution

Update the function try_import() within inheritance_diagram.py to correctly split the full object name at the correct point so that it contains the correct module name(s) and class/classes.

This could be done a few different ways, but I don't think a regex like the one used on line 63-65 is enough to work by itself. The one that comes to my head is to iteratively pop off attribute names off of the string until it is importable, and then return the result of getattr(sys.modules.get(modname), attrname, None).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Open inheritance_diagram.py and inspect try_import(); reproduce the failure with the nested Foo.Bar and Foo.Baz classes and the shown inheritance-diagram directive. Done means the plugin imports classes nested inside classes and no longer emits the reported warning for foo_package.Foo.Bar.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
documentation
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.