-
Notifications
You must be signed in to change notification settings - Fork 76
Add flexible metadata system #580
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
Merged
Merged
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
5c7d118
Add Metadata class and integration tests for parameter metadata handling
tobiasploetz 324867e
Add Metadata to parameters module exports
tobiasploetz 945d0d0
Add changelog entry for metadata feature
tobiasploetz df13f7c
Make changelog entry more concise
tobiasploetz 61338b4
Add missing validators
AdrianSosic ca03d18
Handle None path via optional conversion
AdrianSosic a85ed36
Make field separation type-safe
AdrianSosic f170cc5
Move class definition to right place
AdrianSosic f3dee22
Adjust converter name and signature
AdrianSosic d528bf8
Fix converter docstring
AdrianSosic 06452b7
Drop unnecessary override
AdrianSosic 486dfdd
Activate slots for Metadata class
AdrianSosic b8eaedc
Refactor tests for metadata
tobiasploetz 1430754
Refactor Metadata as a generic class, not specific to Parameter
tobiasploetz 8eddb61
Format changelog entry
AdrianSosic e7e0f23
Format error message
AdrianSosic d57d322
Fix class references in docstrings
AdrianSosic e3c7b05
Reorganize metadata tests
tobiasploetz 1625769
Add serialization tests and hypothesis strategies for Metadata class
tobiasploetz 5b2def4
Add optional metadata generation for parameter hypothesis strategies
tobiasploetz edc1a08
Polish docstring, comments and changelog
AdrianSosic 3c0675e
Clean up metadata core tests
AdrianSosic 268fdd6
Refactor integration tests
AdrianSosic 07c38f6
Move validation test to correct folder
AdrianSosic e6035f4
Add validation test for Metadata class
AdrianSosic d2fe159
Add missing copy operation
AdrianSosic 56c7b45
Add serialization test for field separation
AdrianSosic 9219a96
Fix metadata serialization
AdrianSosic 66e4ac1
Fix field query for Metadata subclassing
AdrianSosic ceec322
Separate logic into Metadata and ParameterMetadata classes
AdrianSosic 1306e47
Split hypothesis strategy
AdrianSosic d7b238a
Generalize serialization to Metadata subclasses
AdrianSosic a90c8a5
Generalize field separation test
AdrianSosic 133e0c2
Switch to ParameterMetadata for existing tests
AdrianSosic 641bd40
Fix type hints for `_explicit_fields`
Scienfitz 8f7a9ce
Update to metadata strategy and tests , CHANGELOG
tobiasploetz df90616
Typos in doc string
tobiasploetz 40f1c6f
Make TypeVar _TMetaData private to module
tobiasploetz c506399
Add entry for docs/_build to .gitignore
tobiasploetz 41330ba
Rename ParameterMetadata to MeasurableMetadata and update references …
tobiasploetz 1a5370b
Add Metadata to Objective and MeasurableMetadata to Target
tobiasploetz e21d7ef
Refactor metadata handling in Objective, Parameter, and Target classe…
tobiasploetz 43aeb20
Update CHANGELOG.md
tobiasploetz f3d758d
Small refactoring of metadata tests
tobiasploetz dfc2380
Fix mypy failure
tobiasploetz 38b4fcf
Update CONTRIBUTERS.md
AdrianSosic 157d251
Add metadata class to parameter and target package namespaces
AdrianSosic c5da6be
Fix changelog entries
AdrianSosic 4c5c4b9
Reuse parent implement of is_empty
AdrianSosic ec150d1
Extend ranges of metadata component strategies
AdrianSosic 82009eb
Harmonize formatting
AdrianSosic 031ab64
Drop unnecessary test for non-None metadata
AdrianSosic 331af4e
Drop unnecessary serialization tests
AdrianSosic 57ab9c9
Drop unnecessary test for independent metadata objects
AdrianSosic 59b45d6
Remove unnecessary boilerplate by parametrizing integration tests
AdrianSosic e261749
Parametrize is_empty tests
AdrianSosic e7e4134
Drop unnecessary target and objective validation tests
AdrianSosic 50f7ff0
Add virtual environment directories to .gitignore
AdrianSosic f3760d9
Update scikit-fingerprints URL
AdrianSosic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| """Generic metadata system for BayBE objects.""" | ||
|
Scienfitz marked this conversation as resolved.
|
||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Any, TypeVar | ||
|
|
||
| import cattrs | ||
| from attrs import AttrsInstance, define, field, fields | ||
| from attrs.validators import deep_mapping, instance_of | ||
| from attrs.validators import optional as optional_v | ||
| from typing_extensions import override | ||
|
|
||
| from baybe.serialization import SerialMixin, converter | ||
| from baybe.utils.basic import classproperty | ||
|
|
||
| _TMetaData = TypeVar("_TMetaData", bound="Metadata") | ||
|
|
||
|
|
||
| @define(frozen=True) | ||
|
AVHopp marked this conversation as resolved.
|
||
| class Metadata(SerialMixin): | ||
| """Metadata class providing basic information for BayBE objects.""" | ||
|
|
||
| description: str | None = field( | ||
| default=None, validator=optional_v(instance_of(str)) | ||
| ) | ||
| """A description of the object.""" | ||
|
|
||
| misc: dict[str, Any] = field( | ||
| factory=dict, | ||
| validator=deep_mapping( | ||
| mapping_validator=instance_of(dict), | ||
| key_validator=instance_of(str), | ||
| # FIXME: https://github.com/python-attrs/attrs/issues/1246 | ||
| value_validator=lambda *x: None, | ||
| ), | ||
| kw_only=True, | ||
| ) | ||
| """Additional user-defined metadata.""" | ||
|
|
||
| @misc.validator | ||
| def _validate_misc(self, _, value: dict[str, Any]) -> None: | ||
| if inv := set(value).intersection(self._explicit_fields): | ||
| raise ValueError( | ||
| f"Miscellaneous metadata cannot contain the following fields: {inv}. " | ||
| f"Use the corresponding attributes instead." | ||
| ) | ||
|
|
||
| @classproperty | ||
| def _explicit_fields(cls: type[AttrsInstance]) -> set[str]: | ||
| """The explicit metadata fields.""" # noqa: D401 | ||
| flds = fields(cls) | ||
| return {fld.name for fld in flds if fld.name != flds.misc.name} | ||
|
AdrianSosic marked this conversation as resolved.
|
||
|
|
||
| @property | ||
| def is_empty(self) -> bool: | ||
| """Check if metadata contains any meaningful information.""" | ||
| return self.description is None and not self.misc | ||
|
|
||
|
|
||
| @define(frozen=True) | ||
| class MeasurableMetadata(Metadata): | ||
| """Class providing metadata for BayBE :class:`Parameter` objects.""" | ||
|
|
||
| unit: str | None = field(default=None, validator=optional_v(instance_of(str))) | ||
| """The unit of measurement for the parameter.""" | ||
|
|
||
| @override | ||
| @property | ||
| def is_empty(self) -> bool: | ||
| """Check if metadata contains any meaningful information.""" | ||
| return super().is_empty and self.unit is None | ||
|
|
||
|
|
||
| def to_metadata( | ||
| value: dict[str, Any] | _TMetaData, cls: type[_TMetaData], / | ||
| ) -> _TMetaData: | ||
| """Convert a dictionary to :class:`Metadata` (with :class:`Metadata` passthrough). | ||
|
|
||
| Args: | ||
| value: The metadata input. | ||
| cls: The specific :class:`Metadata` subclass to convert to. | ||
|
|
||
| Returns: | ||
| The created metadata instance of the requested :class:`Metadata` subclass. | ||
|
|
||
| Raises: | ||
| TypeError: If the input is not a dictionary or of the specified | ||
| :class:`Metadata` type. | ||
| """ | ||
| if isinstance(value, cls): | ||
| return value | ||
|
|
||
| if not isinstance(value, dict): | ||
| raise TypeError( | ||
| f"The input must be a dictionary or a '{cls.__name__}' instance. " | ||
| f"Got: {type(value)}" | ||
| ) | ||
|
|
||
| # Separate known fields from unknown ones | ||
| return converter.structure(value, cls) | ||
|
|
||
|
|
||
| @converter.register_structure_hook | ||
| def _separate_metadata_fields(dct: dict[str, Any], cls: type[Metadata]) -> Metadata: | ||
| """Separate known fields from miscellaneous metadata.""" | ||
| dct = dct.copy() | ||
| explicit = {fld: dct.pop(fld, None) for fld in cls._explicit_fields} | ||
| return cls(**explicit, misc=dct) | ||
|
|
||
|
|
||
| @converter.register_unstructure_hook | ||
| def _flatten_misc_metadata(metadata: Metadata) -> dict[str, Any]: | ||
| """Flatten the metadata for serialization.""" | ||
| cls = type(metadata) | ||
| fn = cattrs.gen.make_dict_unstructure_fn(cls, converter) | ||
| dct = fn(metadata) | ||
| dct = dct | dct.pop(fields(Metadata).misc.name) | ||
| return dct | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| """Hypothesis strategies for metadata.""" | ||
|
|
||
| import hypothesis.strategies as st | ||
| from hypothesis import assume | ||
|
|
||
| from baybe.utils.metadata import MeasurableMetadata, Metadata | ||
|
|
||
| _descriptions = st.one_of(st.none(), st.text(min_size=0)) | ||
| """A strategy generating metadata descriptions.""" | ||
|
|
||
|
|
||
| @st.composite | ||
| def _miscs(draw: st.DrawFn, cls: type[Metadata]): | ||
| """Generates miscellaneous metadata for various metadata classes.""" | ||
| misc = draw( | ||
| st.dictionaries( | ||
| st.text(min_size=0), | ||
| st.one_of(st.text(), st.integers(), st.floats(allow_nan=False)), | ||
|
AVHopp marked this conversation as resolved.
|
||
| max_size=5, | ||
| ) | ||
| ) | ||
| assume(not cls._explicit_fields.intersection(misc)) | ||
| return misc | ||
|
|
||
|
|
||
| @st.composite | ||
| def metadata(draw: st.DrawFn): | ||
| """Generate :class:`baybe.utils.metadata.Metadata`.""" | ||
| description = draw(_descriptions) | ||
| misc = draw(_miscs(Metadata)) | ||
| return Metadata(description=description, misc=misc) | ||
|
|
||
|
|
||
| @st.composite | ||
| def measurable_metadata(draw: st.DrawFn): | ||
| """Generate :class:`baybe.parameters.base.MeasurableMetadata`.""" | ||
| description = draw(_descriptions) | ||
| unit = draw(st.one_of(st.none(), st.text(min_size=0))) | ||
| misc = draw(_miscs(MeasurableMetadata)) | ||
| return MeasurableMetadata(description=description, unit=unit, misc=misc) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AdrianSosic could you please add ".env" as another exception as well? Did some cleaning and setup of my local IDE and that would be super helpful :)