lucsorel / py2puml

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

extract methods #11

Open mskoenz opened 3 years ago

mskoenz commented 3 years ago

First: awesome package, thx a lot ^^

Second: am I not finding the option, or is is currently not possible to extract the methods of classes?

Best regards mskoenz

lucsorel commented 3 years ago

Hi @mskoenz,

Thank you for your interest, I am glad if this library is of any help to you.

Unfortunately you are right: py2puml does not extract the methods of the parsed classes yet. I initially wrote it to document a folder of tangled dataclasses representing the domain model of an application that I started to work for. I needed to see composition relationships between the dataclasses.

But it makes totally sense to improve the library so that it exports methods too. Would you like to contribute?

jonykalavera commented 2 years ago

I'd like to give this a try. will create a pull request. @lucsorel: any ideas you already have about this?

jonykalavera commented 2 years ago

Some basic goal would be to cover this point in the plantuml docs https://plantuml.com/class-diagram#090967fbee930909

jonykalavera commented 2 years ago

how should it look?

@startuml
class Example {
   some_attr : int
   some_method(some_param: int = 1) -> date
}
@enduml
jonykalavera commented 2 years ago

Singature could be done as the first example here https://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object

>>> from inspect import signature
>>> def foo(a, *, b:int, **kwargs):
...     pass

>>> sig = signature(foo)

>>> str(sig)  # LIKE THIS
'(a, *, b:int, **kwargs)'

>>> str(sig.parameters['b'])
'b:int'

>>> sig.parameters['b'].annotation
<class 'int'>
jonykalavera commented 2 years ago

will wait for feedback to proceed. ✌️ py2puml parsing

lucsorel commented 2 years ago

hi @jonykalavera Thank you for proposing your help, which I appreciate a lot. I was on holidays but I am back. However, I am not available a lot these days (young dad + busy time for the vegetable garden). Please, don't take it personally and be patient if I am slow to answer back :pray:

A couple answers and thoughts:

  1. yes, signature inspection seems to me the way to go
  2. about the way to type signature parameters and the return type, I am not sure
    1. you suggested some_method(some_param: int = 1) -> date which seems pythonesque (with the arrow)
    2. but the UML convention seems to be date some_method(some_param: int = 1) and void method_returning_nothin(some_param: int = 1); but I would prefer leaving the return type empty rather than indicating void (unless the return type is set to -> None in the code), because using type annotations for the return type of methods is not a common practice. What do you think?
  3. about relationships: if a.some_method() returns an instance of class B, should we draw an UML relationship between classes A and B? If yes, should it be an association relationship? I would suggest not to add relationship at all when handling methods and leave them for the constructors, what do you think?
  4. we should distinguish instance/class methods from static methods. I would add {static} in front of static methods only, and leave class methods without UML decoration: class methods (receiving the class as their first parameter) do not exist in the UML syntax
  5. when I created this library, I was interested only in the class/instance attributes because they tell how the business domain is structured. I am ok with adding the methods since it is a wish of the community. However, I am wondering whether it is time to introduce some options to py2puml to let the people express what should be output (attributes, methods, etc.). It may be premature to decide what is optional (and what would be the default choice!), but I would like to know what you think about that, if you please
jonykalavera commented 2 years ago

hi @jonykalavera Thank you for proposing your help, which I appreciate a lot. I was on holidays but I am back. However, I am not available a lot these days (young dad + busy time for the vegetable garden). Please, don't take it personally and be patient if I am slow to answer back 🙏

No problem. I assume you do this in your free time. :)

A couple answers and thoughts:

  1. yes, signature inspection seems to me the way to go 👍

  2. about the way to type signature parameters and the return type, I am not sure

  3. you suggested some_method(some_param: int = 1) -> date which seems pythonesque (with the arrow)

yeah, I think it is ok because, when no explicit {method} is set, the distinguishing criteria is the presence of the parenthesis. and I like it sticks to the python typing syntax. Although for our purposes it's probably better to explicitly prefix them with {method} anyway.

  1. but the UML convention seems to be date some_method(some_param: int = 1) and void method_returning_nothin(some_param: int = 1);

AFAIK there are no hard rules about the typing order; at least according to the docs: https://plantuml.com/class-diagram#090967fbee930909

Note that the syntax is highly flexible about type/name order.

but I would prefer leaving the return type empty rather than indicating void (unless the return type is set to -> None in the code), because using type annotations for the return type of methods is not a common practice. What do you think? I think we should reflect the pythonic way to show it since the documentation Well I think if it is there we should show it

I'm with you. In short: let's not use void, I like staying pythonic. let's leave type empty if not hinted, and show it if it is. IMHO I think regardless of general practices the tool should just reflect what is there. for me, the value of this tool is that it can be a trusty mirror that lets me constantly reflect upon the architecture and general shape of my project at a high level. WDYT?

  1. about relationships: if a.some_method() returns an instance of class B, should we draw a UML relationship between classes A and B? If yes, should it be an association relationship? I would suggest not to add relationships at all when handling methods and leave them for the constructors, what do you think?

I agree. but maybe I am biased. in any case, I think we should leave it out of the scope of this PR.

  1. we should distinguish instance/class methods from static methods. I would add {static} in front of static methods only, and leave class methods without UML decoration: class methods (receiving the class as their first parameter) do not exist in the UML syntax

I agree. it should not be too hard to do. similar to how we dealt with abstract classes. also, I noticed that the signature shows the self and cls parameters too. which at first I thought to remove but then maybe they are useful to distinguish them. WDYT?

  1. when I created this library, I was interested only in the class/instance attributes because they tell how the business domain is structured. I am ok with adding the methods since it is a wish of the community. However, I am wondering whether it is time to introduce some options to py2puml to let the people express what should be output (attributes, methods, etc.). It may be premature to decide what is optional (and what would be the default choice!), but I would like to know what you think about that if you please

I totally agree. we should provide customization options. especially if the pr about functions gets merged. I want that one but i can imagine some might not. for now, if we want to keep the expected behavior, we can add one option to the command default false.

Will wait for your comments to proceed

lucsorel commented 2 years ago

sorry for the delay, the house is covid19 positive here. It seems to me that the PR lacks some unit tests (for the generation of the UML signature). And could you have a look at what needs to be done in the Contributions and version update section, please, when you have time?

grayfox88 commented 1 year ago

Hi @lucsorel , I'd like to help here and write some unit tests. Do you know how to proceed when the implementation already exists in @jonykalavera 's forked repository?

lucsorel commented 1 year ago

hi @grayfox88, thank you for popping in and proposing your help :smiley:

The work started in PR #30 and what remains to do is listed in this comment: https://github.com/lucsorel/py2puml/pull/30#issuecomment-1135503929. The add-methods branch needs rebasing and merging, I modified some core functionalities (homogenized the way type annotations are processed, particularly).

There was also the issue that using inspection (stuffs like an_object.__dict__) is that inspect.getmembers(a_class, callable) also yields all the dunder methods associated to inherited methods from extending a native Python class (like extending a dictionary, for example). Filtering them on the presence of '__' around the method names would remove user-defined dunder methods (which is possible, although not recommended by the community).

I suspect that this feature should document only the methods declared in the class from the methods inherited from parent classes. Thus, I wonder whether we should keep this inspection approach or switch to an analysis of the abstract syntax tree :shrug:

Adding unit tests that would check the output of py2puml on these cases (extending a dictionary, extending a custom parent class with user-defined methods, a class with user-defined dunder methods, etc.) would help understanding what we want (or accept) as PlantUML outputs. Do you feel like adding them without touching what @jonykalavera did?

grayfox88 commented 1 year ago

New PR created https://github.com/lucsorel/py2puml/pull/43

stiebels commented 1 year ago

Hi @lucsorel @grayfox88 , Would love to see #43 merged. Any help needed?

lucsorel commented 1 year ago

hi all :smiley:

PR https://github.com/lucsorel/py2puml/pull/51 needs to be reviewed and merged first because it also introduces github actions in the project, including formatting and linting hooks. I would like to enforce an homogeneity of code style in this project as feature requests and contributions are coming. Feel free to review it to accelerate the process :pray:

Then, I will rebase and contribute on this PR.

stiebels commented 1 year ago

Alright, @lucsorel! I've reviewed the entire PR (took a bit...) and left a handful of comments.