Pyreverse UML with complex type hints (Union, List, ...)

1.9k Views Asked by At

I am trying to generate a UML diagram for a class that uses Unions and List typehints.

An example of it would be

from dataclasses import dataclass
from typing import Union   

@dataclass
class ClassA:
    name: str    

@dataclass
class ClassB:
    an_attribute: int    

@dataclass
class ClassC:
    my_class: Union[ClassA, ClassB]

When running pyreverse -ASmn stackoverflow_example.py -o png I obtain an UML not showing usages of classA and classB:

UML class diagram with three unconnected classes A,B and C

If I replace the code in ClassC to

@dataclass
class ClassC:
    my_class: ClassA

Then I get an UML description closer to my goal: UML class diagram, with 3 classes A,B and C, C being composed with A

But of course, this means that when I assign something of type ClassB to my_class I get a warning highlighted, which is exactly what it should do. My understanding of how the Union should work is that I would get something like this: UML class diagram manually edited, with 3 classes A,B and C, C being composed with A and with B

The example shows Union, but List and others will have similar behaviours.

Is there a way of doing this, or is this a design that shouldn't be done?

1

There are 1 best solutions below

8
Christophe On

How should it be in UML?

According to PEP 484 that specifies type hinting:

A type factored by Union[T1, T2, ...] is a supertype of all types T1, T2, etc., so that a value that is a member of one of these types is acceptable for an argument annotated by Union[T1, T2, ...].

But in UML, this would require an intermediary type, because there can be only one my_class member:

enter image description here

How to get it in pyreverse?

Looking at the different options of pyreverse, there is not much we can do:

  • You may add the option -b for including built-in types. In this case, you'll see that all the classes inherit from object, and you'll see int and str including composition of members of that type. But Union stays desperately as it is, and it does not show anything like PEP 484 suggest. This suggest that this feature is not supported by pyreverse.

  • Pyreverse shows exactly the same behavior for Union, as if you'd add a type that does not exist (e.g. xxxx or even nonsense such as xxx [1,2,yyy]): it indicates the string as it is in the source file but doesn't draw the corresponding objects. This reinforces the impression of lack of support.

  • In a last experience, I processed the example file with the typing.py package together in pyreverse. It identified nicely the dependency between packages. But still no change for union.

  • Using the alternative syntax ClassA | ClassB leads to a slightly different diagram, as type information is completely removed but not composition is shown either.

I can only conclude that the graphical rendering of unions does not seem supported by this tool.

Workaround for unions?

The Union is a practical feature but that is not very object oriented:

  • UML has no direct support for it, and you’d need to deal with it as explained above.
  • Union group together potentially unrelated types, and this does not facilitate polymorphic programming.

Fortunately there is an easy replacement: make A and B subclass of a common empty class, say AB, and make my_class an AB. Pyreverse then generates the expected result:

enter image description here