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

ORM: factor out attributes/extras code into mixin classes for front- end backend #4376

Merged
merged 3 commits into from
Sep 22, 2020

Conversation

mbercx
Copy link
Member

@mbercx mbercx commented Sep 16, 2020

Currently, the methods for attributes and extras of the Node backend entity classes are implemented for both the Django and SqlAlchemy backend classes (DjangoNode and SqlaNode). Since the methods are very similar, it's better to homogenize them and introduce mixin classes which we can add as parents to the BackendNode class. This has the added advantage that in case we want to add attributes or extras to other entities, we only have to add either mixin class as a parent to the backend entity class to achieve this.

We can do the same for the front-end Node class, however here we only create the mixin class for the extras, as we are not planning to add attributes to other entities for now, we only add the EntityExtrasMixin class, since the plan is to add extras to the Group entity (see #4328).

This PR does the following:

  • Homogenize the attributes/extras methods for the DjangoNode and SqlaNode backend classes.
  • Move all methods of the DjangoNode and SqlaNode backend classes related to attributes and extras to two mixin classes: AttributesBackendEntity and ExtrasBackendEntity. Add these as parent classes to the BackendNode class.`
  • Move the methods of the Node front-end class related to extras to the EntityExtrasMixin class.

@mbercx mbercx requested a review from sphuber September 16, 2020 14:48
@mbercx
Copy link
Member Author

mbercx commented Sep 16, 2020

@sphuber thanks for all the help with the reviewing so far! I still have two questions:

  • As you can see, I've only made a front-end mixin class for the extras, as we don't plan to add attributes to other entities for now. It might be a little strange to make a mixin class for the attributes, when these methods are currently only used for one front-end entity. However, it would be more consistent to do so. What do you think?
  • I can't seem to fix this pylint issue related to the __all__variable in aiida.orm.implementation.backends (undefined-all-variable). Can you spot what I'm missing here? 😅

@mbercx mbercx changed the title Feature/attributes extras mixin Add attributes/extras mixin classes Sep 16, 2020
@mbercx mbercx force-pushed the feature/attributes_extras_mixin branch 2 times, most recently from 0832962 to bcfad38 Compare September 17, 2020 07:46
@codecov
Copy link

codecov bot commented Sep 17, 2020

Codecov Report

Merging #4376 into develop will decrease coverage by 0.03%.
The diff coverage is 94.16%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4376      +/-   ##
===========================================
- Coverage    79.32%   79.30%   -0.02%     
===========================================
  Files          468      470       +2     
  Lines        34757    34635     -122     
===========================================
- Hits         27569    27465     -104     
+ Misses        7188     7170      -18     
Flag Coverage Δ
#django 73.16% <93.85%> (+0.21%) ⬆️
#sqlalchemy 72.36% <92.62%> (+0.20%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
aiida/backends/general/abstractqueries.py 63.71% <ø> (ø)
aiida/orm/implementation/sql/__init__.py 100.00% <ø> (ø)
aiida/orm/implementation/sql/backends.py 100.00% <ø> (ø)
aiida/orm/implementation/sqlalchemy/groups.py 87.12% <0.00%> (ø)
aiida/orm/utils/node.py 91.18% <0.00%> (-0.90%) ⬇️
aiida/orm/implementation/django/nodes.py 90.43% <25.00%> (-3.99%) ⬇️
aiida/orm/implementation/sqlalchemy/nodes.py 92.16% <25.00%> (-3.17%) ⬇️
aiida/backends/sqlalchemy/manager.py 78.00% <33.34%> (ø)
aiida/tools/data/cif.py 82.36% <50.00%> (ø)
aiida/orm/implementation/groups.py 72.31% <75.00%> (ø)
... and 21 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 12be9ad...f2dc964. Read the comment docs.

@mbercx mbercx force-pushed the feature/attributes_extras_mixin branch from bcfad38 to 2326620 Compare September 17, 2020 08:05
@sphuber
Copy link
Contributor

sphuber commented Sep 17, 2020

I could have sworn I typed what I am about to type yesterday evening already, but apparently it didn't get through. So I will redo it :)

thanks for all the help with the reviewing so far! I still have two questions:

Right back at you! Great work so far.

As you can see, I've only made a front-end mixin class for the extras, as we don't plan to add attributes to other entities for now. It might be a little strange to make a mixin class for the attributes, when these methods are currently only used for one front-end entity. However, it would be more consistent to do so. What do you think?

I see your point, but there is an additional advantage of separating it into a separate file: it actually makes the code more readable. As it stands, the Node class is huge and contains a lot of implementation. Having the full attribute functionality in a separate class, the mixin, will make the code easier to work with. In addition, this brings it on par with the extras, even though (for now) the attributes mixin will only be used by the Node. I think this is fine though and the preferable solution.

* I can't seem to fix this pylint issue related to the __all__variable in `aiida.orm.implementation.backends` (`undefined-all-variable`). Can you spot what I'm missing here?

I have no idea. It looks fine. This might actually just be a pylint bug. These are notoriously hard to fix or simply even get rid off. I will try to do some testing locally.

In the meantime, since this PR will need to keep more than 1 commit, and so I won't be able to squash, it will be important to have the commits exactly as they will have to be merged. Your first two commits are already perfect. I would just suggest to add a third on top of that, that simply creates the front end mixins. Then you just have to put the fixes of the additional commits and rebase them in the respective commits where they are supposed to go.

Finally, I would maybe prefix each commit title with "ORM:" such that it is immediately clear what part of the code it touches.
E.g.:

  • ORM: homogenize attributes/extras methods of backend node
  • ORM: move attributes/extras methods of backend node to mixins
  • ORM: move attributes/extras methods of frontend node to mixins

@mbercx mbercx force-pushed the feature/attributes_extras_mixin branch 3 times, most recently from edd041c to a6d5354 Compare September 19, 2020 11:07
@mbercx
Copy link
Member Author

mbercx commented Sep 19, 2020

I see your point, but there is an additional advantage of separating it into a separate file: it actually makes the code more readable. As it stands, the Node class is huge and contains a lot of implementation. Having the full attribute functionality in a separate class, the mixin, will make the code easier to work with. In addition, this brings it on par with the extras, even though (for now) the attributes mixin will only be used by the Node. I think this is fine though and the preferable solution.

Agreed, the Node class is pretty huge. 👍

I have no idea. It looks fine. This might actually just be a pylint bug. These are notoriously hard to fix or simply even get rid off. I will try to do some testing locally.

My local pre-commit runs don't actually raise this issue. Might be related to the pylint version we are using?

In the meantime, since this PR will need to keep more than 1 commit, and so I won't be able to squash, it will be important to have the commits exactly as they will have to be merged. Your first two commits are already perfect. I would just suggest to add a third on top of that, that simply creates the front end mixins. Then you just have to put the fixes of the additional commits and rebase them in the respective commits where they are supposed to go.

Done! Let me know if you have any further comments.

@mbercx mbercx added the pr/ready-for-review PR is ready to be reviewed label Sep 19, 2020

from aiida.orm.implementation.utils import clean_value, validate_attribute_extra_key

__all__ = ('BackendEntity', 'BackendCollection', 'EntityType', 'BackendEntityAttributesMixin', \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just small reference for the future (don't need to adapt it here): the slash is not necessary when you are in between parentheses.

@sphuber
Copy link
Contributor

sphuber commented Sep 19, 2020

Thanks a lot @mbercx , this is perfect. All we need to do is figure out this pylint bug and then rebase it on latest develop and we can merge this. I will also see if I can find a solution for the pylint thing, but will have to wait until Tuesday.

Make sure the code for the attributes and extras methods are identical,
as a first step towards refactoring the code to use a mixin class for
these methods. These changes should have no influence on how the methods
function.

Add exception chaining for exceptions raised directly during the handling
of another exception. There is only a minor difference in the output, but
it should make it clear that this exception was raised purposefully.
@sphuber sphuber force-pushed the feature/attributes_extras_mixin branch 4 times, most recently from 69a9a66 to 0b556a7 Compare September 22, 2020 08:04
Move the attributes and extras methods to two mixin classes called
`BackendEntityAttributesMixin` and `BackendEntityExtrasMixin`, stored in
the new aiida.orm.implementation.entities.py module. The mixin classes
rely on the `is_stored` and `_flush_if_stored` methods, so these are
added as abstract methods. They are "mixed in" at the BackendNode` level
where the abstract methods of the attributes and extras are removed.

Move the `_flush_if_stored` method to the `BackendEntity` class, which
is added leftmost to the `BackendNode` parent classes. This method can
be used by all backend entities.

Move `BackendEntity` and `BackendCollection` classes to
`aiida.orm.implementation.entities.py` module.

Move `validate_attribute_extra_key` and `clean_value` methods to new
module `aiida.orm.implementation.utils.py`.

Move the calls to `validates_attribute_extra_key` method from the front
end `Node` class to the backend mixin classes `AttributesBackendEntity`
and `ExtrasBackendEntity`. This way the key/value validation/cleaning is
both done at the backend level, which is more consistent. Moreover, this
means other frontend classes won't have to add this call to
`validates_attribute_extra_key` to their methods, when they want to use
the attributes/extras methods.

Add exception chaining for all the modules that are adjusted.
Move all methods related to attributes and extras from the frontend
`Node` class to separate mixin classes called `EntityAttributesMixin`
and `EntityExtrasMixin`. This makes it easier to add these methods to
other frontend entity classes and makes the code more maintainable.
@sphuber sphuber force-pushed the feature/attributes_extras_mixin branch from 0b556a7 to f2dc964 Compare September 22, 2020 08:25
@sphuber sphuber self-requested a review September 22, 2020 09:09
@sphuber sphuber changed the title Add attributes/extras mixin classes ORM: factor out attributes/extras code into mixin classes for front- end backend Sep 22, 2020
Copy link
Contributor

@sphuber sphuber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @mbercx ! The final problem with the pylint false positive on the __all__ in the aiida.orm.implementation.backends had to do with the fact that the aiida.orm.implementation.sql module also defines a backends module and imports the base backends. Some complex process of import order and potential overwriting, caused pylint to not understand what was going on. Removing the __all__ from the aiida.orm.implementation.sql module fixed it. It is ok because that module is not used outside the backend and doesn't really need a shorthand import path

@sphuber sphuber merged commit 8dec326 into aiidateam:develop Sep 22, 2020
@mbercx mbercx deleted the feature/attributes_extras_mixin branch November 21, 2020 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr/ready-for-review PR is ready to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants