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

Use Pydantic v2 at runtime #584

Draft
wants to merge 3 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
16 changes: 4 additions & 12 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ name: tests

on:
push:
branches:
- "main"
pull_request:
branches:
- "main"
workflow_dispatch:
schedule:
- cron: "0 0 * * *"

Expand All @@ -18,25 +14,22 @@ 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:
OE_LICENSE: ${{ github.workspace }}/oe_license.txt

strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-12
- macos-latest
python-version:
- "3.10"
- "3.11"
pymbar-version:
- "3.1"
pydantic-version:
- "2"
openmm-version:
- "8"
openeye:
Expand All @@ -54,7 +47,6 @@ jobs:
create-args: >-
python=${{ matrix.python-version }}
pymbar=${{ matrix.pymbar-version }}
pydantic=${{ matrix.pydantic-version }}
openmm=${{ matrix.openmm-version }}

- name: Install OpenEye
Expand Down Expand Up @@ -87,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 }}
Expand Down
2 changes: 1 addition & 1 deletion devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies:
- pyyaml
- requests
- python-dateutil
- pydantic >=1.10.17,<3
- pydantic =2
- taproom
- dataclasses
- pandas =1
14 changes: 0 additions & 14 deletions openff/evaluator/_pydantic.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down
12 changes: 2 additions & 10 deletions openff/evaluator/datasets/curation/components/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)


Expand Down
99 changes: 42 additions & 57 deletions openff/evaluator/datasets/curation/components/filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
model_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,
Expand All @@ -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]]
Expand Down Expand Up @@ -155,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):
Expand Down Expand Up @@ -205,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")

if minimum_pressure is not None and maximum_pressure is not None:
assert maximum_pressure > minimum_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

return values
return self


class FilterByPressure(CurationComponent):
Expand Down Expand Up @@ -379,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")

assert allowed_elements is not None or forbidden_elements is not None
assert allowed_elements is None or forbidden_elements is None
@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

return values
return self


class FilterByElements(CurationComponent):
Expand Down Expand Up @@ -445,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):
Expand Down Expand Up @@ -724,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):
Expand Down Expand Up @@ -804,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")
Expand Down Expand Up @@ -978,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")
Expand Down Expand Up @@ -1097,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")
Expand Down
7 changes: 1 addition & 6 deletions openff/evaluator/datasets/curation/components/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.")
Expand Down
2 changes: 1 addition & 1 deletion openff/evaluator/datasets/curation/components/thermoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion openff/evaluator/datasets/curation/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
Loading