Skip to content

Conversation

@aborgna-q
Copy link
Collaborator

@aborgna-q aborgna-q commented Dec 17, 2025

Adds a Metadata protocol similar to the one in the rust side, and exports the rust key definitions.

Depends on #2764

@aborgna-q aborgna-q requested a review from a team as a code owner December 17, 2025 14:46
@aborgna-q aborgna-q requested review from acl-cqc and removed request for a team December 17, 2025 14:46
@codecov
Copy link

codecov bot commented Dec 17, 2025

Codecov Report

❌ Patch coverage is 83.21678% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.71%. Comparing base (1cdd646) to head (abd1361).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
hugr-py/src/hugr/envelope.py 68.88% 14 Missing ⚠️
hugr-py/src/hugr/metadata.py 86.84% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2765      +/-   ##
==========================================
+ Coverage   83.52%   83.71%   +0.18%     
==========================================
  Files         268      267       -1     
  Lines       53162    53145      -17     
  Branches    47871    47730     -141     
==========================================
+ Hits        44405    44488      +83     
+ Misses       6371     6276      -95     
+ Partials     2386     2381       -5     
Flag Coverage Δ
python 88.67% <83.21%> (-0.10%) ⬇️
rust 83.14% <ø> (+0.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

Base automatically changed from ab/pyo3-modules to main December 17, 2025 17:04
Copy link
Contributor

@maximilianruesch maximilianruesch left a comment

Choose a reason for hiding this comment

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

Thanks, this will be a very useful addition! I have a bunch of suggested changes, some more nitty, some questions, some remarks for later.

Comment on lines +128 to +134
@classmethod
def to_json(cls, value: GeneratorDesc) -> dict[str, str]:
return value._to_json()

@classmethod
def from_json(cls, value: Any) -> GeneratorDesc:
return GeneratorDesc._from_json(value)
Copy link
Contributor

Choose a reason for hiding this comment

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

If we have a lot of "simple" metadata keys that do not require special validation (only calling the JSON serialize / deserialize of their wrapped type), we might want to think about creating something like a mixin for such cases to reduce boilerplate / verbosity. I do think we need a bit more data / keys to assess what the best solution for this is, so there is no need to refactor this right now.

Copy link
Collaborator Author

@aborgna-q aborgna-q Dec 18, 2025

Choose a reason for hiding this comment

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

👍

For native-type metadata (ints, strings, lists, and dicts) there's no need to override these methods. But yeah, it'd be nice to have a deserialize/_serialize`
I guess we could use pydantic for that? It's a bit annoying that we're starting from the already-loaded object rather than a json string.

Copy link
Contributor

Choose a reason for hiding this comment

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

I have not yet used pydantic, but it strikes me as very powerful. If we adopt it, we should assess whether there is considerable overhead to serialization / deserialization that is not ignorable in the context of hugr-py.

Comment on lines +148 to +160
@classmethod
def to_json(cls, value: list[ExtensionDesc]) -> list[dict[str, str]]:
return [e._to_json() for e in value]

@classmethod
def from_json(cls, value: Any) -> list[ExtensionDesc]:
if not isinstance(value, list):
msg = (
"Expected UsedExtensions metadata to be a list,"
+ f" but got {type(value)}"
)
raise TypeError(msg)
return [ExtensionDesc._from_json(e) for e in value]
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to the mixin suggestion, lists of wrapped types may be another example of a common pattern that could occur multiple times.

name: str
version: Version | None

def _to_json(self) -> dict[str, str]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it useful to have a protocol for _to_json and _from_json? Also, should these be non-private since they are called from outside the class?

Copy link
Collaborator Author

@aborgna-q aborgna-q Dec 18, 2025

Choose a reason for hiding this comment

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

I wanted to keep the PR simple and focused on the metadata protocol for now.

We should probably look into serializable protocols for the value types. (does pydantic help here?), but I'll leave it for later.

The _to/_from_json shouldn't normally be called by users. I'd love to have a pub(crate) attribute here...

Copy link
Contributor

Choose a reason for hiding this comment

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

While its true that a user has no benefit to calling these methods, there is no harm either, since its either read-only or a constructing method, i.e. they cannot violate any contracts with using these methods. I am unsure as to what the policy of this repo is, i.e. whether a cleaner user interface or adhering to general Python guidelines has a higher priority.

Copy link
Contributor

@maximilianruesch maximilianruesch left a comment

Choose a reason for hiding this comment

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

A preliminary LGTM on the code, save for the last comment on _to_json visibility, and some failing coverage checks (which imo one should address in some way, since the code contains many paths).

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