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

Pre-commit: move yapf and mypy config to pyproject.toml #4996

Closed
wants to merge 2 commits into from

Conversation

sphuber
Copy link
Contributor

@sphuber sphuber commented Jun 25, 2021

The libraries yapf and mypy support the pyproject.toml file for
their configuration starting from v0.31 and v0.9, respectively.

@sphuber sphuber requested a review from chrisjsewell June 25, 2021 08:06
@sphuber
Copy link
Contributor Author

sphuber commented Jun 25, 2021

@chrisjsewell to have support for pyproject.toml required updating mypy which incurred a bunch of new errors in the pre-commit. Don't have time to fix this now, but thought to already create the PR

@codecov
Copy link

codecov bot commented Jun 25, 2021

Codecov Report

Merging #4996 (e05ef2d) into develop (ead3f39) will decrease coverage by 0.01%.
The diff coverage is 86.21%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4996      +/-   ##
===========================================
- Coverage    80.38%   80.37%   -0.00%     
===========================================
  Files          529      529              
  Lines        36862    36871       +9     
===========================================
+ Hits         29627    29633       +6     
- Misses        7235     7238       +3     
Flag Coverage Δ
django 74.88% <86.21%> (-<0.01%) ⬇️
sqlalchemy 73.78% <86.21%> (+0.03%) ⬆️

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

Impacted Files Coverage Δ
aiida/engine/daemon/execmanager.py 66.94% <0.00%> (ø)
aiida/manage/manager.py 80.88% <75.00%> (-0.58%) ⬇️
aiida/manage/configuration/profile.py 94.51% <83.34%> (-0.46%) ⬇️
aiida/engine/processes/calcjobs/manager.py 86.73% <100.00%> (ø)
aiida/orm/nodes/node.py 96.29% <100.00%> (ø)
aiida/tools/graph/graph_traversers.py 88.60% <100.00%> (ø)
aiida/tools/groups/paths.py 91.62% <100.00%> (ø)
aiida/tools/importexport/archive/migrators.py 86.40% <100.00%> (ø)
aiida/tools/importexport/common/config.py 100.00% <100.00%> (ø)
...ida/tools/importexport/dbimport/backends/common.py 97.34% <100.00%> (ø)

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 ead3f39...e05ef2d. Read the comment docs.

@unkcpz
Copy link
Member

unkcpz commented Jul 22, 2021

Hi @sphuber, since I am here to look at mypy errors, I can try to assistant to complete this PR. Do you already start working on it, or can I directly add more commits into your branch?

@sphuber sphuber force-pushed the fix/yapf-mypy-config branch from 736e8d8 to 0bfd323 Compare July 22, 2021 15:41
@sphuber
Copy link
Contributor Author

sphuber commented Jul 22, 2021

@unkcpz I have updated the branch with all that I have currently. It would be great if you could address the mypy errors. Feel free to add commits straight to my branch. Let me know if you don't have rights to do so. Thanks!

@unkcpz
Copy link
Member

unkcpz commented Jul 26, 2021

Hi @sphuber, I make some commits to settle the mypy errors.

Except for the following types of mypy complaints are not resolved:

error: Module has no attribute "Node" [attr-defined]

And lots of similar no attribute errors which I guess are related to the mypy bug as post at python/mypy#10826. mypy was not able to deduce the correct module from __all__.

incompatible with supertype errors

Similar errors of repository_metadata, _updatable_attributes and _hash_ignored_attributes from many files.The signatures are the attributes of parent class and are being overridden as properties in child class. Referring to python/mypy#4125 for more details.

@unkcpz unkcpz force-pushed the fix/yapf-mypy-config branch from f300011 to 9a1e2a9 Compare July 26, 2021 09:15
@sphuber sphuber force-pushed the fix/yapf-mypy-config branch from 13d1996 to 7556343 Compare July 26, 2021 14:41
Copy link
Contributor Author

@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 @unkcpz . Reverted some changes to the Group logic and have some other questions.

backend = self.get_backend()
return Repository(backend=backend)

def get_backend(self) -> 'DiskObjectStoreRepositoryBackend':
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wouldn't call this get_backend. It would be too confusing with the database backend. If we really need this method, a better name would be get_repository_backend. However, why can't we just call get_repository().backend? Is it necessary to have this intermediate method?

Copy link
Member

Choose a reason for hiding this comment

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

a better name would be get_repository_backend

sure!

However, why can't we just call get_repository().backend?

It is because for Profile, get_repository().backend return an AbstractRepositoryBackend which has no attributes such as container defined in it. However, for Profile class, it sticks to the DiskObjectStoreRepositoryBackend, access to rather than the abstract backend class make it possible to deduce the attributes from the backend.

I am not sure if there is any better way to resolve this incompatible mypy error.

Comment on lines 166 to 167
if self._backend is None:
raise ValueError('Could not load the backend.')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would be better to put this check directly after the logic that is supposed to set it, i.e. before the configure_logging call. Second, I don't think a ValueError is the correct exception for this. That is reserved for validation arguments of a method or function. What seems to be happening in this case, the only reason self._backend can be None is because the backend_type is neither BACKEND_DJANGO nor BACKEND_SQLA. So really we shouldn't have this check but add the else clause and raise an AssertionError because our internal logic is supposed to set these when loading a profile and that is what we are checking here: internal consistency of program logic.

Copy link
Member

Choose a reason for hiding this comment

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

Good point!

@@ -206,7 +209,7 @@ def get_backend(self) -> 'Backend':

"""
if self._backend is None:
self._load_backend()
return self._load_backend()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why is this change necessary?

Copy link
Member

@unkcpz unkcpz Jul 27, 2021

Choose a reason for hiding this comment

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

In order to resolve aiida/manage/manager.py:214: error: Incompatible return value type (got "Optional[Backend]", expected "Backend") [return-value].
I assume although the _load_backend() method actually set the self._backend and will never leave it to None, get_backend does not know it and assert the return value self._backend still can be None. mypy can't make an assumption about what the called function is doing.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, I guess this is the place where the type assertion properly takes effect. We have:

return cast('Backend', self._backend)

In that case, I propose _load_backend method should have no return value.

@@ -38,7 +38,7 @@

def import_data_dj(
in_path: str,
group: Optional[Group] = None,
group: Optional[ImportGroup] = None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted this change as it is backwards incompatible. We are currently allowing users to use a plain Group to store nodes in upon importing. We cannot narrow this restriction down.

@@ -66,7 +66,7 @@ def collect_hashkeys(objects, hashkeys):
container_export.export(set(hashkeys), container_profile, compress=True, callback=callback)


def _make_import_group(*, group: Optional[ImportGroup], node_pks: List[int]) -> ImportGroup:
def _make_import_group(*, group: Optional[ImportGroup], node_pks: List[int]) -> Optional[ImportGroup]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To fix the mypy error related to inconsistent types, we should rather change this to Optional[Group]. I have already done this and reverted your other change. Reason why is explained in another comment

@sphuber sphuber force-pushed the fix/yapf-mypy-config branch from 7556343 to 0fe96c8 Compare July 26, 2021 14:57
@unkcpz unkcpz force-pushed the fix/yapf-mypy-config branch from 0fe96c8 to d59452c Compare July 27, 2021 03:17
Copy link
Member

@unkcpz unkcpz left a comment

Choose a reason for hiding this comment

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

@sphuber thanks for review!

I reverted this change as it is backwards incompatible. We are currently allowing users to use a plain Group to store nodes in upon importing. We cannot narrow this restriction down.

Yes, should rollback. After changing that I found it failed some migrate tests.

backend = self.get_backend()
return Repository(backend=backend)

def get_backend(self) -> 'DiskObjectStoreRepositoryBackend':
Copy link
Member

Choose a reason for hiding this comment

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

a better name would be get_repository_backend

sure!

However, why can't we just call get_repository().backend?

It is because for Profile, get_repository().backend return an AbstractRepositoryBackend which has no attributes such as container defined in it. However, for Profile class, it sticks to the DiskObjectStoreRepositoryBackend, access to rather than the abstract backend class make it possible to deduce the attributes from the backend.

I am not sure if there is any better way to resolve this incompatible mypy error.

@@ -206,7 +209,7 @@ def get_backend(self) -> 'Backend':

"""
if self._backend is None:
self._load_backend()
return self._load_backend()
Copy link
Member

@unkcpz unkcpz Jul 27, 2021

Choose a reason for hiding this comment

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

In order to resolve aiida/manage/manager.py:214: error: Incompatible return value type (got "Optional[Backend]", expected "Backend") [return-value].
I assume although the _load_backend() method actually set the self._backend and will never leave it to None, get_backend does not know it and assert the return value self._backend still can be None. mypy can't make an assumption about what the called function is doing.

Comment on lines 166 to 167
if self._backend is None:
raise ValueError('Could not load the backend.')
Copy link
Member

Choose a reason for hiding this comment

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

Good point!

from aiida.repository import Repository

backend = self.get_backend()
Copy link
Member

Choose a reason for hiding this comment

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

Looking at failed CI, found here is a mistake. In case of disturbing you with too many push to your branch, I will fix this later with more valid commits.

Suggested change
backend = self.get_backend()
backend = self.get_repository_backend()

@sphuber sphuber force-pushed the fix/yapf-mypy-config branch from d59452c to 53ff345 Compare July 27, 2021 08:41
The libraries `yapf` and `mypy` support the `pyproject.toml` file for
their configuration starting from v0.31 and v0.9, respectively.

Co-authored-by: Jason Eu <morty.yu@yahoo.com>
@sphuber sphuber force-pushed the fix/yapf-mypy-config branch from 53ff345 to c342428 Compare July 27, 2021 08:46
@chrisjsewell
Copy link
Member

And lots of similar no attribute errors which I guess are related to the mypy bug as post at python/mypy#10826. mypy was not able to deduce the correct module from __all__.

Well in fairness to mypy, these __all__ + import * constructs are basically a bit of a nightmare for static analysers.

We have to use pylint: disable=wildcard-import in all these files, which would suggest it is maybe not the best practice, and it also makes it quite difficult to see what APIs are being exposed at each level (#4511)

Additionally, it currently does not work with new default VS Code Python Language Server (Pylance) and so you get no auto-completions, etc.
Although here, following the issues: microsoft/pylance-release#289 -> microsoft/pyright#1877 -> https://github.com/microsoft/pyright/blob/main/docs/typed-libraries.md#library-interface, there is a work-around; to restructure the __all__ construction like:

__all__ = ()
__all__ += calculation.__all__
__all__ += process.__all__ 
__all__ += workflow.__all__

this does not help with mypy though

@sphuber
Copy link
Contributor Author

sphuber commented Jul 28, 2021

Ok, well if this is a known problem of mypy, is there not some way to ignore these errors in a simple manner, i.e., without having to add inline disable comments on each respective line?

@chrisjsewell
Copy link
Member

#5061 updates mypy to v0.901 😄

@chrisjsewell chrisjsewell added pr/blocked PR is blocked by another PR that should be merged first and removed pr/blocked PR is blocked by another PR that should be merged first labels Aug 11, 2021
@chrisjsewell
Copy link
Member

right lets see where we are at with this

@chrisjsewell
Copy link
Member

Ok well the Module has no attribute errors are now gone, but yeh for the rest; I would suggest the code changes should go in a separate PR and this one should just be about moving the configuration

@sphuber
Copy link
Contributor Author

sphuber commented Aug 11, 2021

I would suggest the code changes should go in a separate PR

What code changes? As far as I know, any code changes applied were simply to address the new errors that were now thrown by the updated version of mypy, so they would be necessary. And there are still a lot of errors left over.

backend = self.get_repository_backend()
return Repository(backend=backend)

def get_repository_backend(self) -> 'DiskObjectStoreRepositoryBackend':
Copy link
Member

@chrisjsewell chrisjsewell Aug 11, 2021

Choose a reason for hiding this comment

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

what code changes

well you are introducing completely new methods 😬

@chrisjsewell
Copy link
Member

chrisjsewell commented Aug 11, 2021

any code changes applied were simply to address the new errors that were now thrown by the updated version of mypy

yeh but after #5061, I think these may be the cause of the errors, rather than fixes; this PR no longer actually updates the mypy version, so in principle moving the configuration should result in zero changes to the mypy outcome.

I'm happy to give it a try when I have time

@chrisjsewell
Copy link
Member

moving the configuration should result in zero changes to the mypy outcome

😅 #5066

@sphuber sphuber deleted the fix/yapf-mypy-config branch August 11, 2021 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants