-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Refactor pyreverse Association Logic #10397
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
base: main
Are you sure you want to change the base?
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the addition! π
I only left two remarks for cases where at least from the comment it seems like the relationship is processed by the wrong handler. Am I missing something?
It would be great if you can add tests for all possible output formats as all printers where changed. You can specify multiple output formats for a single functional test in the [testoptions]
.
Thanks for the review. I agree that the logic is not complete and it needs some more work and I need to figure out all the possible edge cases. Also I think some of the other tests have changed , and I need to rebase aswell. I think this will take some time, but lets see how it goes :) |
This comment has been minimized.
This comment has been minimized.
@Julfried please ping me once the PR is ready to be reviewed again. No hurry though, just want to point this out as I am not actively monitoring the open PRs and instead rely more on the email notifications. π |
This comment has been minimized.
This comment has been minimized.
Codecov Reportβ Patch coverage is
β Your patch check has failed because the patch coverage (98.26%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #10397 +/- ##
==========================================
- Coverage 95.91% 95.88% -0.03%
==========================================
Files 176 176
Lines 19147 19227 +80
==========================================
+ Hits 18364 18436 +72
- Misses 783 791 +8
π New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@DudeNr33 Finally ready for review again. All functional tests are passing and the association logic is working correctly now. Interestingly, I figured while playing around that this PR also fixes #10333 (duplicate arrows issue) as a side effect. The root cause was the same - improper relationship classification led to the same attribute being processed multiple times across different relationship types. Our priority-based processing with duplicate prevention solves this at the source. This example from #10333: class A:
def __init__(self) -> None:
self.var = 2
class B:
def __init__(self) -> None:
self.a_obj = A()
def func(self):
self.a_obj = A()
self.a_obj = A() Now correctly produces single composition relationship instead of duplicates: classDiagram
class A {
var : int
}
class B {
a_obj
func()
}
A --* B : a_obj Should I add the functional test cases from #10333 to this PR, or handle them in a follow-up? The PR is getting quite large, but its up to you. The core association logic is solid and ready for review either way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, that looks pretty good !
I think it's better to add functional test cases for the other issue here as you fixed it here. Usually we do another MR because we don't realise an issue is fixed until later. |
This comment has been minimized.
This comment has been minimized.
I updated the functional tests, so now this also Closes #9267 The example from #8046 currently produces this output: @startuml classes_toy_code
set namespaceSeparator none
class "ExampleClass" as parser.toy_code.ExampleClass {
example1 : Optional[int]
example2 : Optional[int]
}
@enduml However I think this is not the output the was discussed in the issue, since the class level attributes are still not handled correctly. Let me know what you think :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, although I'll let @DudeNr33 review the fine details. Great work clearly a lot of care went into this. Could you try to cover the missing lines maybe ?
Thanks for the review, I appreciate the kind words :) I remember the patch coverage initially dropped because I updated a base class, and only two lines were uncovered at that point. Now I see more lines are reported as missing β could this be because my branch is not up to date with main? Would it make sense to try a rebase to see if that clears it up? |
Yes, good idea ! |
β¦w pyreverse correctly extracts Dummy as Association
β¦ instead of assoiciation)
β¦ass level attributes separate
β¦s composition instead of association)
β¦ fixes last failing test
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
π€ According to the primer, this change has no effect on the checked open source code. π€π This comment was generated for commit 04f4b2e |
Not sure why the workflow hangs? |
Sometime codecov is capricious like that. (Probably nothing to do with the PR itself) |
The same thing is happening for other PRs aswell, so it is definitly not related to the changes. Not sure what is happening here |
Glad I am not the only one that is seeing this. |
Any timeline from the admins to accept this pull request? |
I'd rather have a review from Andreas before merging and he might be in the wilderness without network vacationning atm. It's going to be in pylint 4.0 with high certainty (released around python 3.14 release). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all the work that went into this, and sorry for the late review.
This looks very polished now. I only have one minor remark for the arrow direction of Associations. The rest looks good to me!
} | ||
class P { | ||
} | ||
P --> Association : x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P --> Association : x | |
P <-- Association : x |
I think this should be the other way around according to this article.
Here is some more explanation what the arrow direction indicates: navigability, meaning you can get from the class Aggregation
to P
, but not the other way around.
However the second article also states this is not so relevant in class diagrams and the arrow can be omitted alltogether. In theory there will certainly be also cases where the arrow should be pointing both ways technically.
I'm fine with both options (arrow head pointing towards P
as well as not drawing a head at all) - whatever you prefer.
I assume you refer to the example in the initial issue description, i.e.: from typing import Optional
class ExampleClass():
example1 : Optional[int] = None
example2 : Optional[int] = None
def __init__(self):
self.example1 = 1
self.example2 = 2 In my opinion the current behavior is fine. Finding the right balance between
is not easy. If we want to properly differentiate between static (i.e. class) attributes and non-static (i.e. instance) attributes, we should consequently also differentiate between instance, class ( |
Type of Changes
Description
Closes #9045
Closes #9267
Summary
Fixed incorrect UML semantics in pyreverse for aggregation vs composition relationships. The previous implementation had no explicit
CompositionsHandler
class and did not differentiate between Aggregation and Composition properly, treating most relationships as aggregation by default.Problem
Pyreverse previously lacked proper UML relationship semantics. So I would proposes the following distinction for Python class relationships:
fields.py
fields.mmd
Changes Made
1. Added CompositionsHandler
Created dedicated handler following chain of responsibility pattern:
CompositionsHandler β AggregationsHandler β AssociationsHandler
2. Updated Printers
EdgeType.COMPOSITION
enum value3. Prevented Duplicate Relationships
Modified extract_relationships() to process relationships by priority and avoid duplicates.
Currently I only made the modifications needed to implement the new Associations Logic. If you like the proposed distinction I would work on making the tests pass :)