lucsorel / py2puml

Generate PlantUML class diagrams to document your Python application.
https://pypi.org/project/py2puml/
MIT License
212 stars 34 forks source link

[Bug] parsed an inherited constructor from another module (namespace error) #15

Closed doyou89 closed 3 years ago

doyou89 commented 3 years ago

Problem

A keyword parameter of the parent class have an annotation, but the type of this parameter does not revealed in the file of child class.

A.py

class SystemSession:
    pass

class BaseShell:
    def __init__(self, session: SystemSession = None):
        self.session = session
        self.is_started = False

B.py

from .A import BaseShell

class Nfvo(BaseShell):
    pass

Error Message

Traceback (most recent call last):
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/__main__.py", line 4, in <module>
    run()
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/cli.py", line 17, in run
    print(''.join(py2puml(args.path, args.module)))
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/py2puml.py", line 18, in py2puml
    inspect_module(
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/inspection/inspectmodule.py", line 61, in inspect_module
    inspect_domain_definition(definition_type, root_module_name, domain_items_by_fqn, domain_relations)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/inspection/inspectmodule.py", line 46, in inspect_domain_definition
    inspect_class_type(
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/inspection/inspectclass.py", line 97, in inspect_class_type
    instance_attributes, compositions = parse_class_constructor(class_type, class_type_fqn, root_module_name)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/parseclassconstructor.py", line 29, in parse_class_constructor
    visitor.visit(constructor_ast)
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/ast.py", line 371, in visit
    return visitor(node)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 98, in generic_visit
    NodeVisitor.generic_visit(self, node)
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/ast.py", line 379, in generic_visit
    self.visit(item)
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/ast.py", line 371, in visit
    return visitor(node)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 109, in visit_FunctionDef
    self.generic_visit(node)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 98, in generic_visit
    NodeVisitor.generic_visit(self, node)
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/ast.py", line 379, in generic_visit
    self.visit(item)
  File "/home/doyou89/miniconda3/envs/jupyter/lib/python3.8/ast.py", line 371, in visit
    return visitor(node)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 149, in visit_Assign
    self.extend_relations(full_namespaced_definitions)
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 82, in extend_relations
    self.uml_relations_by_target_fqn.update({
  File "/p4_ws/doyou89.jung/workspace/projects/github/py2puml/py2puml/parsing/astvisitors.py", line 85, in <dictcomp>
    if target_fqn.startswith(self.root_fqn) and target_fqn not in self.uml_relations_by_target_fqn
AttributeError: 'NoneType' object has no attribute 'startswith'

Cause

In the parent class "BaseShell", session's type is "SystemSession". But this "SystemSession" does not exist in the namespace of B.py

lucsorel commented 3 years ago

thank you for the report? I will have a look at it this week

lucsorel commented 3 years ago

ok, I figured out what was the problem: when handling class Nfvo, py2puml sees that the type has an __init__ method, but it is the one of the parent constructor (Nfvo does not have its own __init__), and thus should not be processed. I have to add some safety checks in parse_class_constructor. The pb exists (without the exception, though) for a simpler use-case (the Point.__init__ constructor should not be processed when parsing MetricOrigin):

class Point:
    def __init__(self, x: float=0, y: float=0):
        self.x = x
        self.y = y

class MetricOrigin(Point):
    unit: 'm'

Thank you for spotting this issue :pray: