diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 328441bf..0ce5b5f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_install_hook_types: [pre-commit, commit-msg] repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.4.7 hooks: - id: ruff args: [--fix] @@ -23,7 +23,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell stages: [commit, commit-msg] @@ -46,7 +46,7 @@ repos: - svelte - repo: https://github.com/pre-commit/mirrors-eslint - rev: v9.3.0 + rev: v9.4.0 hooks: - id: eslint types: [file] diff --git a/pyproject.toml b/pyproject.toml index 87dadf4a..c409f492 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,9 +76,6 @@ ignore = [ "PLR", # pylint refactor "PLW2901", # Outer for loop variable overwritten by inner assignment target "PT006", # pytest-parametrize-names-wrong-type - "PT011", # pytest-raises-too-broad - "PT013", # pytest-incorrect-pytest-import - "PT019", # pytest-fixture-param-without-value "PTH", # prefer Path to os.path "S108", "S301", # pickle can be unsafe diff --git a/tests/test_converter.py b/tests/test_converter.py index 1e8c7575..10a86381 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -1,14 +1,16 @@ from __future__ import annotations -from typing import Literal +from typing import TYPE_CHECKING, Literal import pytest from pymatgen.core import Lattice, Structure -from pytest import CaptureFixture from chgnet.graph import CrystalGraph from chgnet.graph.converter import CrystalGraphConverter +if TYPE_CHECKING: + from collections.abc import Generator + lattice = Lattice.cubic(4) species = ["Na", "Cl"] coords = [[0, 0, 0], [0.5, 0.5, 0.5]] @@ -16,7 +18,7 @@ @pytest.fixture() -def _set_make_graph() -> None: +def _set_make_graph() -> Generator[None, None, None]: # fixture to force make_graph to be None and then restore it after test from chgnet.graph import converter @@ -63,7 +65,7 @@ def test_crystal_graph_converter_warns(): @pytest.mark.parametrize("on_isolated_atoms", ["ignore", "warn", "error"]) def test_crystal_graph_converter_forward( - on_isolated_atoms, capsys: CaptureFixture[str] + on_isolated_atoms, capsys: pytest.CaptureFixture[str] ): atom_graph_cutoff = 5 converter = CrystalGraphConverter( @@ -75,13 +77,15 @@ def test_crystal_graph_converter_forward( strained.apply_strain(5) graph_id = "strained" err_msg = ( - f"Structure {graph_id=} has 2 isolated atom(s) with " + f"Structure {graph_id=} has {len(NaCl)} isolated atom(s) with " f"{atom_graph_cutoff=}. " f"CHGNet calculation will likely go wrong" ) if on_isolated_atoms == "error": - with pytest.raises(ValueError) as exc_info: + with pytest.raises( + ValueError, match=f"Structure {graph_id=} has {len(NaCl)} isolated atom" + ) as exc_info: converter.forward(strained, graph_id=graph_id) assert err_msg in str(exc_info.value) else: diff --git a/tests/test_encoders.py b/tests/test_encoders.py index ec66d8bb..6beda801 100644 --- a/tests/test_encoders.py +++ b/tests/test_encoders.py @@ -58,11 +58,9 @@ def test_angle_encoder(num_angular: int, learnable: bool) -> None: @pytest.mark.parametrize("num_angular", [-2, 8]) def test_angle_encoder_num_angular(num_angular: int) -> None: - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=f"{num_angular=} must be an odd integer"): AngleEncoder(num_angular=num_angular) - assert f"{num_angular=} must be an odd integer" in str(exc_info.value) - @pytest.mark.parametrize("learnable", [True, False]) def test_bond_encoder_learnable(learnable: bool) -> None: diff --git a/tests/test_md.py b/tests/test_md.py index 4689a44a..b97115b0 100644 --- a/tests/test_md.py +++ b/tests/test_md.py @@ -15,7 +15,7 @@ from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Structure from pymatgen.io.ase import AseAtomsAdaptor -from pytest import MonkeyPatch, approx +from pytest import MonkeyPatch, approx # noqa: PT013 from chgnet import ROOT from chgnet.graph import CrystalGraphConverter diff --git a/tests/test_model.py b/tests/test_model.py index 5ca7e5f9..df5da7fe 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -3,7 +3,6 @@ import numpy as np import pytest from pymatgen.core import Structure -from pytest import mark from chgnet import ROOT from chgnet.graph import CrystalGraphConverter @@ -14,13 +13,13 @@ model = CHGNet.load() -@mark.parametrize("atom_fea_dim", [1, 64]) -@mark.parametrize("bond_fea_dim", [1, 64]) -@mark.parametrize("angle_fea_dim", [1, 64]) -@mark.parametrize("num_radial", [1, 9]) -@mark.parametrize("num_angular", [1, 9]) -@mark.parametrize("n_conv", [1, 4]) -@mark.parametrize("composition_model", ["MPtrj", "MPtrj_e", "MPF"]) +@pytest.mark.parametrize("atom_fea_dim", [1, 64]) +@pytest.mark.parametrize("bond_fea_dim", [1, 64]) +@pytest.mark.parametrize("angle_fea_dim", [1, 64]) +@pytest.mark.parametrize("num_radial", [1, 9]) +@pytest.mark.parametrize("num_angular", [1, 9]) +@pytest.mark.parametrize("n_conv", [1, 4]) +@pytest.mark.parametrize("composition_model", ["MPtrj", "MPtrj_e", "MPF"]) def test_model( atom_fea_dim: int, bond_fea_dim: int, @@ -118,8 +117,8 @@ def test_predict_structure() -> None: assert out["atom_fea"].shape == (8, 64) -@mark.parametrize("axis", [[0, 0, 1], [1, 1, 0], [-2, 3, 1]]) -@mark.parametrize("rotation_angle", [5, 30, 45, 120]) +@pytest.mark.parametrize("axis", [[0, 0, 1], [1, 1, 0], [-2, 3, 1]]) +@pytest.mark.parametrize("rotation_angle", [5, 30, 45, 120]) def test_predict_structure_rotated(rotation_angle: float, axis: list) -> None: from pymatgen.transformations.standard_transformations import RotationTransformation diff --git a/tests/test_relaxation.py b/tests/test_relaxation.py index 4f85f6f2..87241f59 100644 --- a/tests/test_relaxation.py +++ b/tests/test_relaxation.py @@ -8,7 +8,6 @@ import torch from ase.filters import ExpCellFilter, Filter, FrechetCellFilter from pymatgen.core import Structure -from pytest import approx, mark, param from chgnet.graph import CrystalGraphConverter from chgnet.model import CHGNet, StructOptimizer @@ -54,19 +53,20 @@ def test_relaxation( assert len(traj) == 2 if algorithm == "legacy" else 4 # make sure final structure is more relaxed than initial one - assert traj.energies[-1] == approx(-58.94209, rel=1e-4) + assert traj.energies[-1] == pytest.approx(-58.94209, rel=1e-4) -no_cuda = mark.skipif(not torch.cuda.is_available(), reason="No CUDA device") +no_cuda = pytest.mark.skipif(not torch.cuda.is_available(), reason="No CUDA device") # skip in macos-14 M1 CI due to OOM error (TODO investigate if # PYTORCH_MPS_HIGH_WATERMARK_RATIO can fix) -no_mps = mark.skipif( +no_mps = pytest.mark.skipif( not torch.backends.mps.is_available() or "CI" in os.environ, reason="No MPS device" ) -@mark.parametrize( - "use_device", ["cpu", param("cuda", marks=no_cuda), param("mps", marks=no_mps)] +@pytest.mark.parametrize( + "use_device", + ["cpu", pytest.param("cuda", marks=no_cuda), pytest.param("mps", marks=no_mps)], ) def test_structure_optimizer_passes_kwargs_to_model(use_device: str) -> None: relaxer = StructOptimizer(use_device=use_device)