Skip to content

added data version mixin - #727

Open
chenkasirer wants to merge 1 commit into
mainfrom
data_version
Open

added data version mixin#727
chenkasirer wants to merge 1 commit into
mainfrom
data_version

Conversation

@chenkasirer

Copy link
Copy Markdown
Contributor

Adding library version to data when serializing TimberModel with warning when deserializing data created by a different compas timber version.

What type of change is this?

  • Bug fix in a backwards-compatible manner.
  • New feature in a backwards-compatible manner.
  • Breaking change: bug fix or new feature that involve incompatible API changes.
  • Other (e.g. doc update, configuration, etc)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I added a line to the CHANGELOG.md file in the Unreleased section under the most fitting heading (e.g. Added, Changed, Removed).
  • I ran all tests on my computer and it's all green (i.e. invoke test).
  • I ran lint on my computer and there are no errors (i.e. invoke lint).
  • I added new functions/classes and made them available on a second-level import, e.g. compas_timber.datastructures.Beam.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation, including updating class_diagrams.rst (if appropriate).

Copilot AI review requested due to automatic review settings March 26, 2026 15:10
@codecov

codecov Bot commented Mar 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.74%. Comparing base (92d4f81) to head (b93cf9a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #727      +/-   ##
==========================================
- Coverage   78.75%   78.74%   -0.01%     
==========================================
  Files          74       75       +1     
  Lines       10171    10188      +17     
==========================================
+ Hits         8010     8023      +13     
- Misses       2161     2165       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds version metadata to TimberModel’s serialized data so consumers can detect (and be warned about) potential incompatibilities when deserializing models created with a different compas_timber version.

Changes:

  • Introduces DataVersionMixin to inject a __version__ field into __data__ and warn on version mismatch during __from_data__.
  • Updates TimberModel to use the mixin in its inheritance chain so model serialization/deserialization gains version awareness.
  • Adds unit tests covering matching/mismatching versions and legacy data without a version key; updates changelog.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/compas_timber/data.py Adds DataVersionMixin that appends __version__ to serialized data and warns on mismatched versions during load.
src/compas_timber/model.py Applies DataVersionMixin to TimberModel via multiple inheritance to enable versioned serialization.
tests/compas_timber/test_data_version.py Adds tests validating no-warning roundtrips, mismatch warnings, and legacy payload behavior.
CHANGELOG.md Documents the new serialization version field and mismatch warning behavior.

Comment thread src/compas_timber/data.py
Comment on lines +9 to +12
"""
This mixin adds versioning information to the data representation of a class,
allowing for compatibility checks when loading data created with different versions of the library.
"""

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class docstring starts with a leading newline and doesn’t follow the project’s numpy-style docstring convention used elsewhere (e.g., Parameters/Returns/Notes sections). Consider rewriting it with a one-line summary on the opening line and numpy-style structure so generated docs stay consistent.

Suggested change
"""
This mixin adds versioning information to the data representation of a class,
allowing for compatibility checks when loading data created with different versions of the library.
"""
"""Mixin that adds version metadata to COMPAS Timber data containers.
This class extends :class:`compas.data.Data` to embed the current
``compas_timber`` package version into the serialized data. The version
is written when exporting data and checked when loading it back.
Notes
-----
The version is stored under the ``"__version__"`` key in the data
dictionary so that compatibility checks can be performed when
deserializing.
"""

Copilot uses AI. Check for mistakes.
Comment thread src/compas_timber/data.py
if version != compas_timber.__version__:
warn(
f"Data was created with compas_timber version {version}, but you are using version {compas_timber.__version__}. "
"This may lead to incompatibilities and errors. Consider updating the data or using an older version of compas_timber."

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version-mismatch warning is emitted without an explicit warning category or stacklevel, which means the warning will point at this mixin rather than the deserialization call site. Consider specifying a category (e.g., UserWarning) and stacklevel (often 2) to make the warning more actionable for callers.

Suggested change
"This may lead to incompatibilities and errors. Consider updating the data or using an older version of compas_timber."
"This may lead to incompatibilities and errors. Consider updating the data or using an older version of compas_timber.",
UserWarning,
2,

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +56
# Parse, remove the version key, and re-serialize
import json

raw = json.loads(data_str)

def _strip_version(obj):
if isinstance(obj, dict):
obj.pop("__version__", None)
for v in obj.values():
_strip_version(v)
elif isinstance(obj, list):
for v in obj:
_strip_version(v)

_strip_version(raw)

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test removes "version" recursively from the entire JSON payload, which could accidentally strip unrelated "version" fields if they ever appear elsewhere in the serialized structure. To better model legacy TimberModel data, consider removing the key only from the TimberModel’s own data dict (the object under the root dtype’s "data"), leaving other objects untouched.

Suggested change
# Parse, remove the version key, and re-serialize
import json
raw = json.loads(data_str)
def _strip_version(obj):
if isinstance(obj, dict):
obj.pop("__version__", None)
for v in obj.values():
_strip_version(v)
elif isinstance(obj, list):
for v in obj:
_strip_version(v)
_strip_version(raw)
# Parse, remove the model-level __version__ key, and re-serialize
import json
raw = json.loads(data_str)
data_dict = raw.get("data")
if isinstance(data_dict, dict):
data_dict.pop("__version__", None)

Copilot uses AI. Check for mistakes.
Comment thread src/compas_timber/data.py
def __from_data__(cls, data):
version = data.pop("__version__", None)
if version is not None:
if version != compas_timber.__version__:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger for any version difference, including patch version differences. Ideally, we should trigger warnings only based on semver semantics, i.e. versions higher or equal within the same major are ok, warnings are triggered if major+minor is lower, or if major is different.

Comment thread src/compas_timber/data.py

@classmethod
def __from_data__(cls, data):
version = data.pop("__version__", None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is non-application data, but using dunder names in json is a bit foreign. The problem is that __version__ (or version) belongs to the same level at which dtype is, but this is injecting it inside data and that's somewhat conflictive.

@gonzalocasas gonzalocasas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixin approach is cool, but is it going to be used anywhere else except TimberModel? Unless the plan is to upstream this. But assuming it's not used anywhere else, I would instead prefer to code the version stuff inside TimberModel directly. Also, I would try to tackle the concern of placing metadata in data, and would suggest that we overload __jsondump__(..) and @classmethod __jsonload__(..) in TimberModel and add the version field at the meta data level

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants