From 85ed3b66e69a9f0209154b965d60935822391ba3 Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Mon, 4 Nov 2024 15:37:24 -0600 Subject: [PATCH 1/3] Drop Intel macOS from CI --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b429bcc2..039efa10 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,7 +29,7 @@ jobs: matrix: os: - ubuntu-latest - - macos-12 + - macos-latest python-version: - "3.10" - "3.11" From 0d863a3fc7889d33339a95bc752b0c3c97bd265d Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Mon, 4 Nov 2024 16:18:47 -0600 Subject: [PATCH 2/3] Use Pydantic v2 API --- .github/workflows/ci.yaml | 5 +--- openff/evaluator/_pydantic.py | 14 ---------- .../test_curation/test_filtering.py | 2 +- .../curation/components/components.py | 2 +- .../curation/components/conversion.py | 12 ++------ .../datasets/curation/components/filtering.py | 28 ++++++++----------- .../datasets/curation/components/selection.py | 7 +---- .../datasets/curation/components/thermoml.py | 2 +- .../evaluator/datasets/curation/workflow.py | 2 +- 9 files changed, 19 insertions(+), 55 deletions(-) delete mode 100644 openff/evaluator/_pydantic.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 039efa10..30bbeb3e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: test: - name: 💻 ${{ matrix.os }}, 🐍 ${{ matrix.python-version }}, 👀 ${{ matrix.openeye }}, pymbar ${{ matrix.pymbar-version }}, Pydantic ${{ matrix.pydantic-version }}, OpenMM ${{ matrix.openmm-version }} + name: 💻 ${{ matrix.os }}, 🐍 ${{ matrix.python-version }}, 👀 ${{ matrix.openeye }}, pymbar ${{ matrix.pymbar-version }}, OpenMM ${{ matrix.openmm-version }} runs-on: ${{ matrix.os }} env: @@ -35,8 +35,6 @@ jobs: - "3.11" pymbar-version: - "3.1" - pydantic-version: - - "2" openmm-version: - "8" openeye: @@ -54,7 +52,6 @@ jobs: create-args: >- python=${{ matrix.python-version }} pymbar=${{ matrix.pymbar-version }} - pydantic=${{ matrix.pydantic-version }} openmm=${{ matrix.openmm-version }} - name: Install OpenEye diff --git a/openff/evaluator/_pydantic.py b/openff/evaluator/_pydantic.py deleted file mode 100644 index a3d32444..00000000 --- a/openff/evaluator/_pydantic.py +++ /dev/null @@ -1,14 +0,0 @@ -from pydantic.v1 import ( - BaseModel, - Field, - HttpUrl, - PositiveFloat, - PositiveInt, - ValidationError, - confloat, - conint, - conlist, - constr, - root_validator, - validator, -) diff --git a/openff/evaluator/_tests/test_datasets/test_curation/test_filtering.py b/openff/evaluator/_tests/test_datasets/test_curation/test_filtering.py index 09be395c..fed328c6 100644 --- a/openff/evaluator/_tests/test_datasets/test_curation/test_filtering.py +++ b/openff/evaluator/_tests/test_datasets/test_curation/test_filtering.py @@ -4,8 +4,8 @@ import pandas import pytest from openff.units import unit +from pydantic import ValidationError -from openff.evaluator._pydantic import ValidationError from openff.evaluator.datasets import ( MeasurementSource, PhysicalPropertyDataSet, diff --git a/openff/evaluator/datasets/curation/components/components.py b/openff/evaluator/datasets/curation/components/components.py index 69cb730e..046517cd 100644 --- a/openff/evaluator/datasets/curation/components/components.py +++ b/openff/evaluator/datasets/curation/components/components.py @@ -3,8 +3,8 @@ from typing import overload import pandas +from pydantic import BaseModel -from openff.evaluator._pydantic import BaseModel from openff.evaluator.datasets import PhysicalPropertyDataSet logger = logging.getLogger(__name__) diff --git a/openff/evaluator/datasets/curation/components/conversion.py b/openff/evaluator/datasets/curation/components/conversion.py index 57bab1cc..6d2b1b04 100644 --- a/openff/evaluator/datasets/curation/components/conversion.py +++ b/openff/evaluator/datasets/curation/components/conversion.py @@ -3,25 +3,17 @@ import functools import logging -from typing import TYPE_CHECKING, Union +from typing import Union import pandas +from pydantic import Field, conint from typing_extensions import Literal -from openff.evaluator._pydantic import Field from openff.evaluator.datasets.curation.components import ( CurationComponent, CurationComponentSchema, ) -if TYPE_CHECKING: - conint = int - PositiveInt = int - PositiveFloat = float - -else: - from openff.evaluator._pydantic import conint - logger = logging.getLogger(__name__) diff --git a/openff/evaluator/datasets/curation/components/filtering.py b/openff/evaluator/datasets/curation/components/filtering.py index e82fb476..7ee0e3ed 100644 --- a/openff/evaluator/datasets/curation/components/filtering.py +++ b/openff/evaluator/datasets/curation/components/filtering.py @@ -2,15 +2,24 @@ import itertools import logging from collections import defaultdict -from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union import numpy import pandas from openff.units import unit +from pydantic import ( + Field, + PositiveFloat, + PositiveInt, + confloat, + conint, + constr, + root_validator, + validator, +) from scipy.optimize import linear_sum_assignment from typing_extensions import Literal -from openff.evaluator._pydantic import Field, root_validator, validator from openff.evaluator.datasets.curation.components import ( CurationComponent, CurationComponentSchema, @@ -24,21 +33,6 @@ analyse_functional_groups, ) -if TYPE_CHECKING: - conint = int - confloat = float - PositiveInt = int - PositiveFloat = float - -else: - from openff.evaluator._pydantic import ( - PositiveFloat, - PositiveInt, - confloat, - conint, - constr, - ) - logger = logging.getLogger(__name__) ComponentEnvironments = List[List[ChemicalEnvironment]] diff --git a/openff/evaluator/datasets/curation/components/selection.py b/openff/evaluator/datasets/curation/components/selection.py index 60718e69..dc178b08 100644 --- a/openff/evaluator/datasets/curation/components/selection.py +++ b/openff/evaluator/datasets/curation/components/selection.py @@ -5,9 +5,9 @@ import numpy import pandas +from pydantic import BaseModel, Field, PositiveInt, conlist, validator from typing_extensions import Literal -from openff.evaluator._pydantic import BaseModel, Field, conlist, validator from openff.evaluator.datasets.curation.components import ( CurationComponent, CurationComponentSchema, @@ -28,16 +28,11 @@ PropertyType = Tuple[str, int] if TYPE_CHECKING: - PositiveInt = int - try: from openeye.oegraphsim import OEFingerPrint except ImportError: OEFingerPrint = None -else: - from openff.evaluator._pydantic import PositiveInt - class State(BaseModel): temperature: float = Field(..., description="The temperature (K) of interest.") diff --git a/openff/evaluator/datasets/curation/components/thermoml.py b/openff/evaluator/datasets/curation/components/thermoml.py index 9f2c31f0..6cf85a9d 100644 --- a/openff/evaluator/datasets/curation/components/thermoml.py +++ b/openff/evaluator/datasets/curation/components/thermoml.py @@ -8,9 +8,9 @@ import pandas import requests +from pydantic import Field, HttpUrl from typing_extensions import Literal -from openff.evaluator._pydantic import Field, HttpUrl from openff.evaluator.datasets.curation.components import ( CurationComponent, CurationComponentSchema, diff --git a/openff/evaluator/datasets/curation/workflow.py b/openff/evaluator/datasets/curation/workflow.py index 00685de8..7eeeb7fc 100644 --- a/openff/evaluator/datasets/curation/workflow.py +++ b/openff/evaluator/datasets/curation/workflow.py @@ -3,8 +3,8 @@ import numpy import pandas +from pydantic import BaseModel, Field -from openff.evaluator._pydantic import BaseModel, Field from openff.evaluator.datasets import PhysicalPropertyDataSet from openff.evaluator.datasets.curation.components import CurationComponent from openff.evaluator.datasets.curation.components.conversion import ( From 81ddfa9e7bdca1c4ec3e29acbf40473ce92954ca Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Tue, 3 Dec 2024 10:03:52 -0600 Subject: [PATCH 3/3] Debug --- .github/workflows/ci.yaml | 9 +-- devtools/conda-envs/test_env.yaml | 2 +- .../datasets/curation/components/filtering.py | 73 ++++++++----------- 3 files changed, 35 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 30bbeb3e..50c98439 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,11 +2,7 @@ name: tests on: push: - branches: - - "main" - pull_request: - branches: - - "main" + workflow_dispatch: schedule: - cron: "0 0 * * *" @@ -25,7 +21,6 @@ jobs: OE_LICENSE: ${{ github.workspace }}/oe_license.txt strategy: - fail-fast: false matrix: os: - ubuntu-latest @@ -84,7 +79,7 @@ jobs: run: python -m pip install . utilities/test_plugins/ - name: Run tests - run: python -m pytest -v --cov=openff openff/evaluator/_tests/ --cov-report=xml --color=yes + run: python -m pytest -vx --cov=openff openff/evaluator/_tests/ --cov-report=xml --color=yes - name: Run (non-GPU) tutorials if: ${{ matrix.pymbar-version == 3.1 }} diff --git a/devtools/conda-envs/test_env.yaml b/devtools/conda-envs/test_env.yaml index aefc4b8a..f21addd2 100644 --- a/devtools/conda-envs/test_env.yaml +++ b/devtools/conda-envs/test_env.yaml @@ -30,7 +30,7 @@ dependencies: - pyyaml - requests - python-dateutil - - pydantic >=1.10.17,<3 + - pydantic =2 - taproom - dataclasses - pandas =1 diff --git a/openff/evaluator/datasets/curation/components/filtering.py b/openff/evaluator/datasets/curation/components/filtering.py index 7ee0e3ed..5a041bec 100644 --- a/openff/evaluator/datasets/curation/components/filtering.py +++ b/openff/evaluator/datasets/curation/components/filtering.py @@ -14,7 +14,7 @@ confloat, conint, constr, - root_validator, + model_validator, validator, ) from scipy.optimize import linear_sum_assignment @@ -149,15 +149,15 @@ class FilterByTemperatureSchema(CurationComponentSchema): description="Retain data points measured for temperatures below this value (K)", ) - @root_validator - def _min_max(cls, values): - minimum_temperature = values.get("minimum_temperature") - maximum_temperature = values.get("maximum_temperature") - - if minimum_temperature is not None and maximum_temperature is not None: - assert maximum_temperature > minimum_temperature + @model_validator(mode="after") + def _min_max(self): + if ( + self.minimum_temperature is not None + and self.maximum_temperature is not None + ): + assert self.maximum_temperature > self.minimum_temperature - return values + return self class FilterByTemperature(CurationComponent): @@ -199,15 +199,12 @@ class FilterByPressureSchema(CurationComponentSchema): description="Retain data points measured for pressures below this value (kPa)", ) - @root_validator - def _min_max(cls, values): - minimum_pressure = values.get("minimum_pressure") - maximum_pressure = values.get("maximum_pressure") + @model_validator(mode="after") + def _min_max(self): + if self.minimum_pressure is not None and self.maximum_pressure is not None: + assert self.maximum_pressure > self.minimum_pressure - if minimum_pressure is not None and maximum_pressure is not None: - assert maximum_pressure > minimum_pressure - - return values + return self class FilterByPressure(CurationComponent): @@ -373,15 +370,12 @@ class FilterByElementsSchema(CurationComponentSchema): "`allowed_elements`", ) - @root_validator - def _validate_mutually_exclusive(cls, values): - allowed_elements = values.get("allowed_elements") - forbidden_elements = values.get("forbidden_elements") + @model_validator(mode="after") + def _validate_mutually_exclusive(self): + assert self.allowed_elements is not None or self.forbidden_elements is not None + assert self.allowed_elements is None or self.forbidden_elements is None - assert allowed_elements is not None or forbidden_elements is not None - assert allowed_elements is None or forbidden_elements is None - - return values + return self class FilterByElements(CurationComponent): @@ -439,14 +433,14 @@ class FilterByPropertyTypesSchema(CurationComponentSchema): "required to have been measured at the same state.", ) - @root_validator - def _validate_n_components(cls, values): - property_types = values.get("property_types") - n_components = values.get("n_components") + @model_validator(mode="after") + def _validate_n_components(self): + property_types = self.property_types + n_components = self.n_components assert all(x in property_types for x in n_components) - return values + return self class FilterByPropertyTypes(CurationComponent): @@ -718,15 +712,12 @@ class FilterBySmilesSchema(CurationComponentSchema): "This option only applies when `smiles_to_include` is set.", ) - @root_validator - def _validate_mutually_exclusive(cls, values): - smiles_to_include = values.get("smiles_to_include") - smiles_to_exclude = values.get("smiles_to_exclude") - - assert smiles_to_include is not None or smiles_to_exclude is not None - assert smiles_to_include is None or smiles_to_exclude is None + @model_validator + def _validate_mutually_exclusive(self): + assert self.smiles_to_include is not None or self.smiles_to_exclude is not None + assert self.smiles_to_include is None or self.smiles_to_exclude is None - return values + return self class FilterBySmiles(CurationComponent): @@ -798,7 +789,7 @@ class FilterBySmirksSchema(CurationComponentSchema): "when `smirks_to_include` is set.", ) - @root_validator + @model_validator def _validate_mutually_exclusive(cls, values): smirks_to_include = values.get("smirks_to_include") smirks_to_exclude = values.get("smirks_to_exclude") @@ -972,7 +963,7 @@ class FilterBySubstancesSchema(CurationComponentSchema): "This option is mutually exclusive with `substances_to_include`.", ) - @root_validator + @model_validator def _validate_mutually_exclusive(cls, values): substances_to_include = values.get("substances_to_include") substances_to_exclude = values.get("substances_to_exclude") @@ -1091,7 +1082,7 @@ def _validate_per_component_environments(cls, value): assert all(len(y) == x for x, y in value.items()) return value - @root_validator + @model_validator def _validate_mutually_exclusive(cls, values): at_least_one_environment = values.get("at_least_one_environment") strictly_specified_environments = values.get("strictly_specified_environments")