diff --git a/environment.yml b/environment.yml index fffb4b14..6a8e6810 100644 --- a/environment.yml +++ b/environment.yml @@ -8,7 +8,7 @@ dependencies: - networkx - numpy - openff-toolkit-base >=0.15.1 - - openff-units >=0.3.1 + - openff-units >=0.3.2 - openmm - pint - pip @@ -22,3 +22,7 @@ dependencies: - typing-extensions - ipywidgets # opt - zstandard + - pip: + - git+https://github.com/openforcefield/openff-units/@define-pydantic-schema + - git+https://github.com/openforcefield/openff-interchange/@fix-pydantic + - pydantic==2.13.* diff --git a/src/gufe/settings/models.py b/src/gufe/settings/models.py index 7627bc17..d7e85889 100644 --- a/src/gufe/settings/models.py +++ b/src/gufe/settings/models.py @@ -96,9 +96,11 @@ def __eq__(self, other: Any) -> bool: # which results in frozen/unfrozen objects not being equal # https://github.com/pydantic/pydantic/blob/2486e068e85c51728c9f2d344cfee2f7e11d555c/pydantic/v1/main.py#L911 if isinstance(other, _BaseModel): - return self.model_dump() == other.model_dump() + return self.model_dump(polymorphic_serialization=True) == other.model_dump(polymorphic_serialization=True) + # TODO: as of pydantic 3.13, polymorphic_serialization is required, or else the default(?) will be used instead? + # https://pydantic.dev/docs/validation/latest/concepts/serialization#polymorphic-serialization else: - return self.model_dump() == other + return self.model_dump(polymorphic_serialization=True) == other class ThermoSettings(SettingsBaseModel): diff --git a/src/gufe/tests/test_models.py b/src/gufe/tests/test_models.py index ee613e03..302dd376 100644 --- a/src/gufe/tests/test_models.py +++ b/src/gufe/tests/test_models.py @@ -80,7 +80,7 @@ def test_settings_schema(): def test_openmmffsettings_schema(): - expected_schema = { + expected_val_schema = { "additionalProperties": False, "description": "Parameters to set up the force field with OpenMM ForceFields\n\n.. note::\n Currently, this stores what is needed for the\n :class:`openmmforcefields.system_generators.SystemGenerator` signature.\n See the `OpenMMForceField SystemGenerator documentation`_ for more details.\n\n\n.. _`OpenMMForceField SystemGenerator documentation`:\n https://github.com/openmm/openmmforcefields#automating-force-field-management-with-systemgenerator", "properties": { @@ -110,6 +110,7 @@ def test_openmmffsettings_schema(): }, "nonbonded_method": {"default": "PME", "title": "Nonbonded Method", "type": "string"}, "nonbonded_cutoff": { + "default": '{"magnitude": 0.9, "units": "nanometer"}', "description": "Cutoff value for short range nonbonded interactions in nm. Compatible units will be converted to nm.", "title": "Nonbonded Cutoff", "type": "number", @@ -118,10 +119,14 @@ def test_openmmffsettings_schema(): "title": "OpenMMSystemGeneratorFFSettings", "type": "object", } - ser_schema = OpenMMSystemGeneratorFFSettings.model_json_schema(mode="serialization") + val_schema = OpenMMSystemGeneratorFFSettings.model_json_schema(mode="validation") - assert ser_schema == expected_schema - assert val_schema == expected_schema + assert val_schema == expected_val_schema + + ser_schema = OpenMMSystemGeneratorFFSettings.model_json_schema(mode="serialization") + expected_ser_schema = expected_val_schema.copy() + expected_ser_schema["properties"]["nonbonded_cutoff"]["default"] = {"val": 0.9, "unit": "nanometer"} + assert ser_schema == expected_ser_schema def test_default_settings(): diff --git a/src/gufe/vendor/openff/interchange/pydantic.py b/src/gufe/vendor/openff/interchange/pydantic.py index 88853fd8..52d70f89 100644 --- a/src/gufe/vendor/openff/interchange/pydantic.py +++ b/src/gufe/vendor/openff/interchange/pydantic.py @@ -15,7 +15,7 @@ class _BaseModel(BaseModel): ) def model_dump(self, **kwargs) -> dict[str, Any]: - return super().model_dump(serialize_as_any=True, **kwargs) + return super().model_dump(**kwargs) def model_dump_json(self, **kwargs) -> str: - return super().model_dump_json(serialize_as_any=True, **kwargs) + return super().model_dump_json(**kwargs)