pythonumlpyreverse

pyreverse not showing composition relationships in the UMLs when using absolute imports


I am having trouble generating UMLs with pyreverse, in particular with composition relationships when classes are not part of the same module, and when using absolute imports.

To illustrate the problem, I have the following two modules a.py and b.py in the same package:

a.py:

from b import B


class A:
    def __init__(self, b):
        self.b: B = b

b.py:

class B:
    pass

When I run a pyreverse command in a terminal from the package, I get the following UML. It does not show the composition relationship between the two classes A and B:

enter image description here

However, when I do a relative import from .b import B in a.py, I get the expected result:

enter image description here

It seems like pyreverse does not recognize in the first case that classes B are the same. To resolve the problem, I have tried to add the absolute path of the package to the environment variable PYTHONPATH. However, this did not resolve the problem.

Does anybody know how I can make pyreverse generate the right relationships in the UMLs when classes are defined in different modules, and when using absolute imports?

I am using python 3.8.8 and pylint version 2.12.2.


Solution

  • Edited answer following additional experimentation

    I'm not a python expert, but after a couple more experiments I think I get the information that you need

    First experiments : no package

    In my first experiment, I used several modules that were not in a package. It appeared when using different ways to do the imports that pyreverse shows only the classes of the modules that are mentioned on the command line.

    While I initially assumed that the issue was related to a stricter import syntax, it turned out that in reality it just worked as designed and documented in the man page: pyreverse shows in the diagram only the classes of the modules listed in the pyreverse command line.

    So with a little project with, using almost your definitions in files main.py,a.py and b.py the easy workaround to get all the classes in the diagram was to use pyresverse main.py a.py b.py on a single command line. It generates the diagram:

    enter image description here

    It appears however that the result also depends on the PYTHONPATH, the current directory from where you call pyreverse and the path used to specify the module, as these elements can influence the finding of the right import.

    Additional experiments : packages

    I renamed then main.py into __init__.py to make a package. I can then use pyreverse providing only the directory name of the package (or . if it's the current working directory). Then all the files of the packages are processed for making the class diagram, withoout a need to enumerate them.

    Using pyreverse on the package from within the package directory works with your import syntax and produced a similar diagram as above. However, running pyreverse from its parent directory produces 2 classes side by side without relationship. This means that we are dealing with two different classes B one of which was not found. Using the relative import solved the issue.

    I found it by the way useful to add option -m y to disambiguate the class names by adding the module name:

    enter image description here

    I could verify experimentally: whenever I get unlinked classes, launching the module in python from the same current working directory as pyreverse caused an import error.