Skip to content

Conversation

danielhollas
Copy link
Collaborator

@danielhollas danielhollas commented Sep 17, 2025

I ran mypy in strict mode on the aiida.cmdline.params module and fixed all issues (mostly untyped functions). Then I enabled stricter settings for this module in pyproject.toml

Extra safety before the click 8.2 update (to which I've become paranoid again 😅 )

Also upgraded mypy to 1.18.1. Changelog promises big performance speedup in this release, and this is what I also see locally! 28s -> 15s without cache and 4.5s -> 0.5 with cache! 🎉
(also removed one type: ignore)

Mypy==1.17.1

$ rm -rf .mypy_cache
$ time pre-commit run -a mypy
real	0m28.563s
user	0m27.509s
sys	0m0.880s
$ time pre-commit run -a mypy
real	0m4.629s
user	0m4.176s
sys	0m0.426s

Mypy==1.18.1

$ rm -rf .mypy_cache
$ time pre-commit run -a mypy
real	0m14.716s
user	0m13.714s
sys	0m0.910s
$ time pre-commit run -a mypy
real	0m0.483s
user 0m0.335s
sys	0m0.147s

Copy link

codecov bot commented Sep 17, 2025

Codecov Report

❌ Patch coverage is 87.20379% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.03%. Comparing base (7255f01) to head (ef114af).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/aiida/cmdline/params/types/path.py 78.95% 4 Missing ⚠️
.../aiida/cmdline/params/options/commands/computer.py 70.00% 3 Missing ⚠️
src/aiida/cmdline/params/types/config.py 75.00% 2 Missing ⚠️
src/aiida/cmdline/params/types/user.py 77.78% 2 Missing ⚠️
src/aiida/cmdline/groups/dynamic.py 85.72% 1 Missing ⚠️
src/aiida/cmdline/params/arguments/overridable.py 83.34% 1 Missing ⚠️
src/aiida/cmdline/params/options/conditional.py 85.72% 1 Missing ⚠️
src/aiida/cmdline/params/options/config.py 85.72% 1 Missing ⚠️
src/aiida/cmdline/params/options/interactive.py 87.50% 1 Missing ⚠️
src/aiida/cmdline/params/options/main.py 75.00% 1 Missing ⚠️
... and 10 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7009      +/-   ##
==========================================
- Coverage   79.04%   79.03%   -0.01%     
==========================================
  Files         566      566              
  Lines       43720    43798      +78     
==========================================
+ Hits        34555    34612      +57     
- Misses       9165     9186      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

import click

if t.TYPE_CHECKING:
from click.decorators import FC
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FC is a TypeVar (type parameter) defined here:

https://github.com/pallets/click/blob/2a0e3ba907927ade6951d5732b775f11b54cb766/src/click/decorators.py#L25

It's not clear whether it's better to import it or redefine it here (since this is a type-checking only import I think it is fine, but I don't know what is a best practice in these cases).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say import is fine. Better than to re-define!

return None
def convert(self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None) -> str:
# NOTE: The return value of click.StringParamType.convert is typed as t.Any,
# but from its implementation its clear that it returns a string.
Copy link
Collaborator Author

@danielhollas danielhollas Sep 18, 2025

Choose a reason for hiding this comment

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

def convert(self, value, param, ctx):
newval = super().convert(value, param, ctx)

# Note: Valid :py:class:`click.ParamType`s need to pass through None unchanged
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was perhaps true in click 7.x but doesn't seem to be true anymore; see docstring of ParamType.convert() method:

https://github.com/pallets/click/blob/2a0e3ba907927ade6951d5732b775f11b54cb766/src/click/types.py#L105

And also the code here that calls the convert method only when the value is not None.
https://github.com/pallets/click/blob/2a0e3ba907927ade6951d5732b775f11b54cb766/src/click/types.py#L89

"""Container class that represents a collection of entries of a particular backend entity."""

ENTITY_CLASS: ClassVar[Type[EntityType]] # type: ignore[misc]
ENTITY_CLASS: ClassVar[Type[EntityType]]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ignore not needed thanks to mypy upgrade 🎉

@danielhollas danielhollas changed the title More typing for cmdline.params.types module More typing for cmdline.params module Sep 18, 2025
Copy link
Contributor

@GeigerJ2 GeigerJ2 left a comment

Choose a reason for hiding this comment

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

If the PR passes strict mypy checking, chances are high that it also passes my review :3

import click

if t.TYPE_CHECKING:
from click.decorators import FC
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say import is fine. Better than to re-define!

Comment on lines 44 to +51
if job_resource_cls is None:
# Odd situation...
return False
return False # type: ignore[unreachable]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can probably be removed in the future...

Comment on lines +19 to +23
if t.TYPE_CHECKING:
try:
from typing import TypeAlias
except ImportError:
from typing_extensions import TypeAlias
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a reminder that we can remove this once we drop py 3.9

T = TypeVar('T')


def type_check(what: T, of_type: Any, msg: 'str | None' = None, allow_none: bool = False) -> 'T | None':
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason for the forward reference quotes here instead of adding from future import annotations?


def test_fail_unreachable_url():
"""Test the parameter in case of a valid URL that cannot be reached."""
# TODO: Mock the request to make this faster
Copy link
Contributor

Choose a reason for hiding this comment

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

waiting for the PR hehe

pre-commit = [
'aiida-core[atomic_tools,rest,tests,tui]',
'mypy~=1.17.0',
'mypy~=1.18.0',
Copy link
Contributor

Choose a reason for hiding this comment

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

Do that in a separate PR? Or fine to do it alongside this one? Not sure what is best practice here...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I proclaim that because there was a no fallout from this upgrade it's okay to do it here :-)

@danielhollas danielhollas merged commit 0d5c4d4 into aiidateam:main Sep 24, 2025
32 checks passed
@danielhollas danielhollas deleted the params-types-typing branch September 24, 2025 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants