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

Apply mypy for checking #594

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,40 @@ repos:
args: [--fix]
# Run the formatter.
- id: ruff-format

- repo: local

hooks:

- id: mypy
name: mypy
entry: mypy
args: [--config-file=setup.cfg]
language: python
types: [python]
require_serial: true
pass_filenames: true
exclude: >-
(?x)^(
.github/.*|
docker/.*|
docs/.*|

tests/.*|
tests_integration/.*|

# Exclude the following files temporarily.
src/aiidalab_qe/app/submission/__init__.py|
src/aiidalab_qe/app/parameters/__init__.py|
src/aiidalab_qe/__main__.py|
src/aiidalab_qe/common/widgets.py|
src/aiidalab_qe/common/setup_pseudos.py|
src/aiidalab_qe/common/process.py|
src/aiidalab_qe/common/setup_codes.py|

# Exclude the following files temporarily for old plugins.
src/aiidalab_qe/plugins/bands/.*.py|
src/aiidalab_qe/plugins/pdos/.*.py|
src/aiidalab_qe/plugins/electronic_structure/.*.py|

)$
7 changes: 0 additions & 7 deletions qe.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,6 @@
"from aiidalab_qe.version import __version__"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
42 changes: 42 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ where = src
dev =
bumpver~=2023.1124
pre-commit~=3.2
mypy~=1.8.0
pytest~=6.2
pytest-regressions~=2.2
pgtest==1.3.1
pytest-cov~=4.0
beautifulsoup4~=4.10
types-beautifulsoup4~=4.10

[options.package_data]
aiidalab_qe.app.parameters = qeapp.yaml
Expand All @@ -57,6 +60,45 @@ aiidalab_qe.properties =
pdos = aiidalab_qe.plugins.pdos:pdos
electronic_structure = aiidalab_qe.plugins.electronic_structure:electronic_structure

[mypy]
disallow_any_generics = false
disallow_incomplete_defs = false
disallow_subclassing_any = false
disallow_untyped_calls = false
# these options reduce the strictness and should eventually be removed
disallow_untyped_defs = false
show_error_codes = true
show_traceback = true
# strictness
strict = true
warn_return_any = false
warn_unreachable = true
no_namespace_packages = true

[mypy-ipywidgets.*]
ignore_missing_imports = true

[mypy-aiidalab_widgets_base.*]
ignore_missing_imports = true

[mypy-aiida_quantumespresso.*]
ignore_missing_imports = true

[mypy-IPython.*]
ignore_missing_imports = true

[mypy-setuptools.*]
ignore_missing_imports = true

[mypy-aiida_pseudo.*]
ignore_missing_imports = true

[mypy-ase.*]
ignore_missing_imports = true

[mypy-nglview.*]
ignore_missing_imports = true

[aiidalab]
title = Quantum ESPRESSO
description = Perform Quantum ESPRESSO calculations
Expand Down
3 changes: 2 additions & 1 deletion src/aiidalab_qe/app/configuration/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import ipywidgets as ipw
import traitlets as tl
import typing as t
from aiida import orm
from aiida_quantumespresso.calculations.functions.create_kpoints_from_distance import (
create_kpoints_from_distance,
Expand Down Expand Up @@ -222,7 +223,7 @@ def update_settings(self, **kwargs):
def get_panel_value(self):
# create the the initial_magnetic_moments as None (Default)
# XXX: start from parameters = {} and then bundle the settings by purposes (e.g. pw, bands, etc.)
parameters = {
parameters: dict[str, t.Any] = {
"initial_magnetic_moments": None,
"pw": {
"parameters": {
Expand Down
21 changes: 12 additions & 9 deletions src/aiidalab_qe/app/configuration/pseudos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import io
import re
import typing as t

import ipywidgets as ipw
import traitlets as tl
Expand All @@ -19,6 +20,9 @@
PseudoFamily,
)

if t.TYPE_CHECKING:
from aiida_pseudo.data.pseudo.upf import UpfData as TypeUpfData

UpfData = DataFactory("pseudo.upf")
SsspFamily = GroupFactory("pseudo.family.sssp")
PseudoDojoFamily = GroupFactory("pseudo.family.pseudo_dojo")
Expand Down Expand Up @@ -174,9 +178,8 @@ def set_text_color(self, change):
self.dft_functional_help,
self.dft_functional_prompt,
):
old_opacity = re.match(
r"[\s\S]+opacity:([\S]+);[\S\s]+", html.value
).groups()[0]
matched = re.match(r"[\s\S]+opacity:([\S]+);[\S\s]+", html.value)
old_opacity = matched.group(1) if matched else 1.0
html.value = html.value.replace(
f"opacity:{old_opacity};", f"opacity:{opacity};"
)
Expand Down Expand Up @@ -344,7 +347,7 @@ def _reset(self):
return

try:
pseudos = pseudo_family.get_pseudos(structure=self.structure)
pseudos = pseudo_family.get_pseudos(structure=self.structure) # type: ignore
# get cutoffs dict of all elements
cutoffs = self._get_cutoffs(pseudo_family)
except ValueError as exception:
Expand All @@ -356,23 +359,23 @@ def _reset(self):
self.pseudos = {kind: pseudo.uuid for kind, pseudo in pseudos.items()}
self.set_pseudos(self.pseudos, cutoffs)

def _get_pseudos_family(self, pseudo_family: str) -> orm.Group:
def _get_pseudos_family(self, pseudo_family: str) -> t.Optional[orm.Group]:
"""Get the pseudo family from the database."""
try:
pseudo_set = (PseudoDojoFamily, SsspFamily, CutoffsPseudoPotentialFamily)
pseudo_family = (
pseudo_family_node = (
orm.QueryBuilder()
.append(pseudo_set, filters={"label": pseudo_family})
.one()[0]
)
return pseudo_family_node

except exceptions.NotExistent as exception:
raise exceptions.NotExistent(
f"required pseudo family `{pseudo_family}` is not installed. Please use `aiida-pseudo install` to"
"install it."
) from exception

return pseudo_family

def _get_cutoffs(self, pseudo_family):
"""Get the cutoffs from the pseudo family."""
from aiida_pseudo.common.units import U
Expand Down Expand Up @@ -462,7 +465,7 @@ class PseudoUploadWidget(ipw.HBox):
def __init__(
self,
kind: str = "",
pseudo: UpfData | None = None,
pseudo: TypeUpfData | None = None,
cutoffs: dict | None = None,
**kwargs,
):
Expand Down
2 changes: 1 addition & 1 deletion src/aiidalab_qe/app/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def _on_click_kill_button(self, _=None):
First kill the process, then update the kill button layout.
"""
workchain = [orm.load_node(self.process)]
control.kill_processes(workchain)
control.kill_processes(workchain) # type: ignore

# update the kill button layout
self._update_kill_button_layout()
Expand Down
7 changes: 4 additions & 3 deletions src/aiidalab_qe/app/result/workchain_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,10 @@ def __init__(self, node, export_dir=None, **kwargs):
self._download_archive_button.on_click(self._download_archive)
self._download_button_container = ipw.Box([self._download_archive_button])

if node.exit_status != 0:
if node.exit_status != 0 and (final_calcjob := self._get_final_calcjob(node)):
title = ipw.HTML(
f"<h4>Workflow failed with exit status [{ node.exit_status }]</h4>"
)
final_calcjob = self._get_final_calcjob(node)
env = Environment()
template = resources.read_text(static, "workflow_failure.jinja")
style = resources.read_text(static, "style.css")
Expand Down Expand Up @@ -224,7 +223,9 @@ def _download_archive(self, _):
# Create archive file.
with TemporaryDirectory() as tmpdir:
self._prepare_calcjob_io(self.node, Path(tmpdir))
shutil.make_archive(fn_archive.with_suffix(""), "zip", tmpdir)
shutil.make_archive(
str(fn_archive.with_suffix("")), "zip", tmpdir
)
Path(fn_lockfile).unlink() # Delete lock file.
except Timeout:
# Failed to obtain lock, presuming some other process is working on it.
Expand Down
13 changes: 6 additions & 7 deletions src/aiidalab_qe/app/submission/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def _update_state(self, _=None):

def _toggle_install_widgets(self, change):
if change["new"]:
self.children = [
self.children = [ # type: ignore
child for child in self.children if child is not change["owner"]
]

Expand Down Expand Up @@ -224,8 +224,8 @@ def _show_alert_message(self, message, alert_class="info"):
def _update_resources(self, change):
if change["new"] and (
change["old"] is None
or orm.load_code(change["new"]).computer.pk
!= orm.load_code(change["old"]).computer.pk
or orm.load_code(change["new"]).computer.pk # type: ignore
!= orm.load_code(change["old"]).computer.pk # type: ignore
):
self.set_resource_defaults(orm.load_code(change["new"]).computer)

Expand Down Expand Up @@ -277,7 +277,7 @@ def _check_resources(self):

num_cpus = self.resources_config.num_cpus.value
on_localhost = (
orm.load_node(self.pw_code.value).computer.hostname == "localhost"
orm.load_node(self.pw_code.value).computer.hostname == "localhost" # type: ignore
)
if self.pw_code.value and on_localhost and num_cpus > 1:
self._show_alert_message(
Expand Down Expand Up @@ -376,7 +376,7 @@ def submit(self, _=None):

self._update_state()

def _generate_label(self) -> dict:
def _generate_label(self) -> str:
"""Generate a label for the work chain based on the input parameters."""
formula = self.input_structure.get_formula()
properties = [
Expand All @@ -392,8 +392,7 @@ def _generate_label(self) -> dict:
else:
properties_info = f"properties on {', '.join(properties)}"

label = "{} {} {}".format(formula, relax_info, properties_info)
return label
return f"{formula} {relax_info} {properties_info}"

def _create_builder(self) -> ProcessBuilderNamespace:
"""Create the builder for the `QeAppWorkChain` submit."""
Expand Down
6 changes: 4 additions & 2 deletions src/aiidalab_qe/common/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
AiiDAlab Team
"""
from __future__ import annotations

import ipywidgets as ipw

DEFAULT_PARAMETERS = {}
DEFAULT_PARAMETERS: dict = {}


class Panel(ipw.VBox):
Expand Down Expand Up @@ -100,7 +102,7 @@ class ResultPanel(Panel):

title = "Result"
# to specify which plugins (outputs) are needed for this result panel.
workchain_labels = []
workchain_labels: list = []

def __init__(self, node=None, **kwargs):
self.node = node
Expand Down
1 change: 1 addition & 0 deletions src/aiidalab_qe/py.typed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Marker file for PEP 561
6 changes: 3 additions & 3 deletions src/aiidalab_qe/workflows/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ def define(cls, spec):
i += 1
spec.outline(
cls.setup,
if_(cls.should_run_relax)(
cls.run_relax,
cls.inspect_relax
if_(cls.should_run_relax)( # type: ignore
cls.run_relax, # type: ignore
cls.inspect_relax # type: ignore
),
cls.run_plugin,
cls.inspect_plugin,
Expand Down