Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document that component attribute must be typed to infer composition relationship #19

Closed
kaungsgit opened this issue Oct 22, 2021 · 5 comments · Fixed by #25
Closed
Assignees
Labels
documentation Improvements or additions to documentation

Comments

@kaungsgit
Copy link

Hi,

I tested a simple composition relationship (Dolphin and DorsalFin) and it doesn't seem to work. Only inheritance relationship is captured in the resulting plantUML. Any idea how the composition relationship can be captured?

class Mammals:
    def __init__(self):
        pass

    def eat(self):
        pass

    def move(self):
        pass

class Dolphin(Mammals):
    def __init__(self):
        super(Dolphin, self).__init__()
        self.fin = DorsalFin(length=1)

    def move(self):
        pass


class DorsalFin:
    def __init__(self, length):
        self.length = length

    def wiggle(self):
        pass
@lucsorel
Copy link
Owner

lucsorel commented Oct 22, 2021

hi @kaungsgit

Quick answer

Use type annotation to type the value assigned to the component attribute. Then py2puml will infer a composition relationship between the compound class and the component class. Two ways to type the component attribute:

  1. in the assignment expressions:
class Dolphin(Mammals):
    def __init__(self):
        super(Dolphin, self).__init__()
        self.fin: DorsalFin = DorsalFin(length=1) # self.fin: DorsalFin
class DorsalFin:
    def __init__(self, length):
        self.length: float = length # self.length: float
  1. in the constructor signatures:
class Dolphin(Mammals):
    def __init__(self, dorsal_fin: DorsalFin): # dorsal_fin: DorsalFin
        super(Dolphin, self).__init__()
        self.fin = dorsal_fin
class DorsalFin:
    def __init__(self, length: float): # length: float
        self.length = length

Explanation

Two kinds of compositions are handled by py2puml:

  1. in dataclasses: py2puml uses inspection to detect the types associated with the attributes
  2. in constructors (which is your case): py2puml parses the abstract syntax tree (AST) to detect the places in the code where a value is associated to an instance attribute

Your use-case is the 2nd case. There are a couple of unit tests that describe what this library can do, but I believe that the home README.md should better document it: what do you think?

  1. unit tests regarding attributes detected in the body of a constructor:
  1. some lower-level unit-tests about the parsing functions used by py2puml to detect attribute assignments:

To sum-up what py2puml handles for now in constructors:

  • when a constructor parameter is assigned to an attribute, py2puml uses the type annotation associated with that parameter (if any)
  • when a local variable is assigned to an attribute, py2puml uses the type annotation associated with that variable (if any)
  • when an expression is assigned to an attribute, py2puml uses the type annotation associated with the attribute (if any)
  • py2puml does not infer type from an expression, like assigning a literal, or the result of a function call (a constructor is a function):
# unhandled types from literals
self.name = 'Suzie' # could be detected as a string
self.age = 18 # could be detected as an int
# unhandled types from function calls
self.dorsal_fin = DorsalFin(12)
self.amount = Add(10, 2)

I agree that it sound silly to ask people to annotate the type of variable receiving the value of a constructor call (as in self.dorsal_fin: DorsalFin = DorsalFin(12)); but when parsing an AST, it is hard to distinguish instructions (and the type of the value they return) like DorsalFin(12) and Add(10, 2).

Is that ok for you?

@kaungsgit
Copy link
Author

@lucsorel , Thank you very much for your detailed answer. I recently started coding with type hints and I find it easier to read so it is no big deal for me.
I read the README but it didn't contain an example on the constructor case, so maybe, it'd be a good idea to include that as well? Maybe a simple example with the constructor will do.
Also, I noticed I cannot see the methods in the uml output. Is there a way to capture the methods as well? Thanks again!

@kaungsgit
Copy link
Author

I just have to say that this is working very well for me with the PlantUML plugin in Pycharm. Once I can capture the methods as well in the resulting puml file, this will be GOLDEN!!! Thank you so much again!

@lucsorel
Copy link
Owner

I read the README but it didn't contain an example on the constructor case, so maybe, it'd be a good idea to include that as well? Maybe a simple example with the constructor will do.

Yes, I should definitely add a documentation section about that. To prevent other users to open issues (thank you for doing it)

Also, I noticed I cannot see the methods in the uml output. Is there a way to capture the methods as well?

For now, the methods are not extracted. My initial goal was to document and understand the complex datastructures handled by an application that I had to maintain, which involved tens of dataclasses (which usually have no methods). Thus, composition and inheritance relationships are handled by py2puml.

However, there is an issue open for this feature request: #11. Feel free to contribute if you want, do not hesitate to ask for guidance 😃

Thank you so much again!

It is my pleasure to see that this library helps people, I hesitated to publish and I am glad I did. Thank you for your feedback, which helps improving it.

@lucsorel lucsorel self-assigned this Oct 23, 2021
@lucsorel lucsorel added the documentation Improvements or additions to documentation label Oct 23, 2021
@kaungsgit
Copy link
Author

Hey @lucsorel , yeah I'm interested in contributing to capture the methods. It'd really make this tool complete for users like myself. I'm new to code inspection so you'll have to give a good amount of guidance there. Also, if you have any ideas on how this can be done, feel free to share as well. Since the attributes are already captured, I'm assuming capturing methods shouldn't be too bad (but I could be wrong).

@lucsorel lucsorel changed the title Composition relationship is not captured Document that component attribute must be typed to infer composition relationship Oct 25, 2021
lucsorel added a commit that referenced this issue Dec 31, 2021
document composition relationship: closes #19
lucsorel added a commit that referenced this issue Dec 31, 2021
* parse constructors wrapped by decorators: closes #24

* document composition relationship: closes #19

* added a contribution guide and pylint as a dev dependency
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants