-
Notifications
You must be signed in to change notification settings - Fork 230
Run typing on orm/{comments,computers}.py and orm/implementation/storage_backend.py #6704
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
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6704 +/- ##
==========================================
- Coverage 79.27% 79.27% -0.00%
==========================================
Files 566 566
Lines 43779 43782 +3
==========================================
+ Hits 34700 34702 +2
- Misses 9079 9080 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
cf1cf48
to
8d0fa45
Compare
src/aiida/orm/computers.py
Outdated
def list_labels(self) -> List[str]: | ||
"""Return a list with all the labels of the computers in the DB.""" | ||
return self._backend.computers.list_names() | ||
return self._backend.computers.list_names() # type: ignore[attr-defined] |
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 am not sure why mypy complains here
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.
src/aiida/orm/computers.py:57: error: "BackendComputerCollection" has no attribute "list_names" [attr-defined]
checking for a valid scheduler. | ||
""" | ||
if not isinstance(mpirun_cmd, (tuple, list)) or not all(isinstance(i, str) for i in mpirun_cmd): | ||
if not isinstance(mpirun_cmd, (tuple, list)) or not all(isinstance(i, str) for i in mpirun_cmd): # type: ignore[redundant-expr] |
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.
Mypy complains that the isinstance check should be always true since the mpirun_cmd is typed at list or tuple, but we obviously want to preserve the runtime check since we don't control all the callers.
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.
Or I think the better change would be we move the mpirun_cmd runtime check when set up the computer? But it seems out the scope of this PR.
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.
This check is in the _mpirun_command_validator
which is called by the validate
function which is in turn called before storing the Computer instance in DB (in function store()
). So I think the code here is in the correct place.
Then my review will not count ;p Just out of curious, the goal of the PR is to add more mypy checking to code base (I am totally in it, thanks! I was thinking maybe we can even have it as a project for this year's GSoC to find someone can working on it ) or you are targeting to bring some new features that may benefit from adding type? |
Haha, fair. I am mostly confused about this code:
Mypy complains about the missing |
Just adding type checking. These files just seemed like low-hanging fruit, they were already mostly typed! What I really don't like is that we have a list of ignored types in pre-commit config file. It's very easy to miss, and to mess with types in files that are not even checked. I think it would be better to have type: ignore comments at the top of the ignored files, which makes it super clear that they are not checked. EDIT: What originally prompted me was the other PR which add checking for the |
How you import the if typing.TYPE_CHECKING:
from aiida.xxx import BackendComment |
I agree, but it is not easy to make typing correct in python. No actually compiler will help us (yes, I am comparing it with Rust 🙈 ). But I am also trying to bring typing to engine part of the code base module by module (like this one #6641). |
Yes, that's what I am doing in this PR. |
9bbfb06
to
042e513
Compare
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 gave my go on this PR, but it would be nice that someone more familiar with orm can give it a look. I pin @edan-bainglass because your name is in the storage part of team responsibilities.
042e513
to
21db8da
Compare
f70d2a6
to
c3bca2c
Compare
@edan-bainglass after the pydantic PR was merged, I needed to add a bunch of extra |
0820342
to
3b39b38
Compare
This reverts commit 88c1a11.
c25d975
to
ffd23ec
Compare
is_attribute=False, | ||
orm_class='core.node', | ||
orm_to_model=lambda comment, _: comment.node.pk, | ||
orm_to_model=lambda comment, _: cast('Comment', comment).node.pk, |
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.
For context if unclear, orm_to_model
is used when converting an ORM object to its model via .to_model()
. The model should be kept serializable, so AiiDA entities are often converted to their pk
value. In this case, that would be the pk
of the Node
associated with the Comment
instance of this Model
. The Model
does not have a reference of its owning Comment
instance, so a validator cannot be used. Hence (I presume), it motivates the custom orm_to_model
feature.
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.
The lambda
function doesn't know what comment
is (or whatever the entity is in any use), hence the need for casting.
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.
Regarding the second argument (which is, I believe, ignored in general), we pass a repository path. I vaguely recall @agoscinski implementing this. If so, @agoscinski could you please briefly explain why, or reference to an existing explanation? 🙏
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.
@edan-bainglass thank you for the context, that is helpful. 🙏
def create( # type: ignore[override] | ||
self, node: 'BackendNode', user: 'BackendUser', content: Optional[str] = None, **kwargs | ||
): | ||
self, node: 'BackendNode', user: 'BackendUser', content: Optional[str] = None, **kwargs: Any | ||
) -> BackendComment: |
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.
Technically breaks Liskov substitution principle.
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.
One solution would be
@abc.abstractmethod
def create(self,
node: 'BackendNode' | None = None,
user: 'BackendUser' | None = None,
content: Optional[str] = None,
**kwargs: Any
) -> BackendComment:
...
and explicitly check that node
and user
exist, complaining otherwise. It will appease Liskov (no required kwargs, as stated in the abstract class), and mypy
by extension (hopefully). But it may confuse the API user - we state they are optional... they are not!
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.
@edan-bainglass yeah thanks for pointing this out. I think I prefer to ignore the mypy error here instead of complicating the function signature (at least for this PR). Perhaps a more holistic fix is needed for this.
btw: Here's the mypy error
src/aiida/orm/implementation/comments.py:79: error: Signature of "create" incompatible with supertype "aiida.orm.implementation.entities.BackendCollection" [override]
src/aiida/orm/implementation/comments.py:79: note: Superclass:
src/aiida/orm/implementation/comments.py:79: note: def create(self, **kwargs: Any) -> BackendComment
src/aiida/orm/implementation/comments.py:79: note: Subclass:
src/aiida/orm/implementation/comments.py:79: note: def create(self, node: BackendNode, user: BackendUser, content: str | None = ..., **kwargs: Any) -> BackendComment
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.
Yep, that's an LSP violation. We can have a look at a "better" solution later as you say
|
||
_CLS_COLLECTION: Type[CollectionType] = Collection # type: ignore[assignment] | ||
_logger = log.AIIDA_LOGGER.getChild('orm.entities') | ||
_logger: Logger = log.AIIDA_LOGGER.getChild('orm.entities') |
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.
Don't you need to do 'Logger'
if imported under TYPE_CHECKING
?
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.
There is from __future__ import annotations
at the top so all types are implicitly converted to strings.
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 think perhaps we are inconsistent about this throughout the code. Likely, from __future__ import annotations
is added to a module at some point but 'SomeType'
is never reduced to SomeType
. Maybe ruff
can auto-fix?
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.
Relevant ruff issue:
astral-sh/ruff#18502
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.
Thanks. Regarding the inconsistency, I think already in this PR they exist - see above change to comments.py
(the casting in orm_to_model
lambdas).
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.
Added a few comments. In general, I'm happy with this, but maybe we can have a call to understand the final goal (the typing mission), and order of operations 🙂
@edan-bainglass thank you for the review. I think I addresses all your comments, PTAL.
Sure, maybe we can talk on Thursdays aiida-core meeting (or after?). tl;dr on my side, I was adding typing to aiida-core just as a side-project for me to learn more about Python typing system. Besides merging the existing PRs I don't have any immediate plans to work on this further in the near future (this has already consumed too much time I should have been spending elsewhere 😅) FWIW: I think the biggest priority (and what I have been doing) is the fact that many files are skipped altogether from type checking (see the list in |
Let's do that, just to sync up.
No worries. I will pick it up, but likely limited to the ORM.
Indeed, needs to be addressed. |
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.
Thanks @danielhollas for the replies. Good on my end. Let's go!
Run typing on
orm/comments.py
,orm/computers.py
andorm/implementation/storage_backend.py
. These files were mostly typed already, but not checked by mypy.Depends on #6703