-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
pyreverse Aggregation and Composition arrows are same #6543
Comments
Please assign this to @qequ |
Do you want me to assign this to you @qequ ? 😅 |
@Pierre-Sassoulas please assign this to me 👍 |
@Pierre-Sassoulas @DudeNr33 I've been working on this and I have some doubts about astroid/pyreverse and the way associations edges are created. The way pyreverse currently works is checking in the dicts of For aggregations, that are loosely coupled relationships and that each object is independent from each other, I think a good way to identify them would be to check if the arguments are objects and using a type hint like this for the previous example # Aggregation
class Student:
def __init__(self, id):
self._id = id
def registration_number(self, department_id):
return str(self._id) + '-' + department_id
class Department:
def __init__(self, id, student: Student):
self._id = id
self._student = student
def student_registration(self):
return self._student.registration_number(self._id)
if __name__ == '__main__':
student = Student(50)
department = Department('ENG', student)
print(department.student_registration()) and then you get a The thing is, if I use type hints over an instance like the following snippet class Department:
def __init__(self, department_id, student_id):
self._id = department_id
self._student: Student = Student(student_id)
def student_registration(self):
return self._student.registration_number(self._id) the attribute _student is identified as a |
My first instinct would be to tell you to use inference ( |
Thanks @qequ for taking this on! I will have to take a closer look at this and hope that I find the time for that on the weekend.
|
@DudeNr33 # Aggregation
class Student:
def __init__(self, id):
self._id = id
def registration_number(self, department_id):
return str(self._id) + '-' + department_id
class Department:
def __init__(self, id, student):
self._id = id
self._student = student
def student_registration(self):
return self._student.registration_number(self._id)
if __name__ == '__main__':
student = Student(50)
department = Department('ENG', student)
print(department.student_registration())
When using type hints like in the example I showed in the previous message About the second point, my first thought was that: finding a way to check if the instance had been created in the instance of the class, but I coudn't find a way to. Neither |
I tried around myself a bit. You will need to inspect the assignattr.parent.value For the following code class Dependency:
pass
class Compositor:
def __init__(self):
self.composition = Dependency()
class Aggregator:
def __init__(self, dep: Dependency):
self.aggregation = dep you should see that the set(assignattr.parent.value.func.infer()) # --> {<ClassDef.Dependency...10ae4c700>} If you do the same for the This is just a hint where and how you could start, feel free to explore different approaches. Also this would only handle the simplest case, where the creation of the object takes place directly in the assignment to the attribute itself. One could think of more complicated examples, like creating the object in a helper method and assigning the return value to the attribute: class Compositor:
def __init__(self):
self.composition = self._create_dep()
def _create_dep(self) -> Dependency:
return Dependency() Or storing the created object first in a intermediate local variable before assigning it: class Compositor:
def __init__(self):
intermediary = Dependency()
self.composition = intermediary Those cases would require more logic. |
@DudeNr33 @Pierre-Sassoulas should we open a new issue for the cases that @DudeNr33 pointed out that pyreverse can´t handle yet? class Compositor:
def __init__(self):
self.composition = self._create_dep()
def _create_dep(self) -> Dependency:
return Dependency() class Compositor:
def __init__(self):
intermediary = Dependency()
self.composition = intermediary |
I don't know if we want to open this can of worms. If we start to handle those two "simple" cases we'll have to handle a large number of arbitrarily hard (or rather Turing complete hard) other use cases. I'll let @DudeNr33 decide. |
We don't have a policy that you must open an issue in order to create a pull request. I would open an issue first if
In other cases feel free to directly open a PR. If the change requires a changelog entry, you can first create the PR itself, and then add a changelog entry referencing the PR number instead of an issue and push that in a second commit. |
Bug description
pyreverse generates same diagram for aggregation and composition. Here is the sample code:
Configuration
No response
Command used
Pylint output
Expected behavior
Different Arrow type.
Source
Pylint version
OS / Environment
Windows 11
WSL2
Additional dependencies
sudo apt install graphviz
The text was updated successfully, but these errors were encountered: