-
Notifications
You must be signed in to change notification settings - Fork 65
FXC-4111-Enable Tidy3dBaseModel.from_file() for various subclasses #2990
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
Conversation
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.
Pull Request Overview
This PR refactors the class loading mechanism by consolidating type-to-class mapping logic from individual classes (like Expression and stub classes) into the base Tidy3dBaseModel class. This allows various simulation and data types to be loaded directly using Tidy3dBaseModel.from_file() without requiring class-specific implementations.
Key changes:
- Moved
TYPE_TO_CLASS_MAPdictionary andparse_objmethod fromExpressiontoTidy3dBaseModel - Simplified
Tidy3dStub.from_file()andTidy3dStubData.from_file()to delegate to base class - Updated documentation strings to use generic type references
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tidy3d/components/base.py | Added global TYPE_TO_CLASS_MAP and parse_obj method to enable polymorphic loading of all subclasses |
| tidy3d/plugins/expressions/base.py | Removed duplicate TYPE_TO_CLASS_MAP and parse_obj implementation now handled by base class |
| tidy3d/web/api/tidy3d_stub.py | Simplified stub loading methods to delegate to Tidy3dBaseModel.from_file() and updated docstrings |
| CHANGELOG.md | Added entry documenting the new functionality |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
4 files reviewed, 2 comments
|
@marcorudolphflex could you have a look at the failing lazy loading test 🙏 |
I have this print statement about the types currently. In both develop and this branch I get |
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.
5 files reviewed, 2 comments
|
I guess because of this change the data[0].class.name is now |
|
If I catch that correctly, the fundamental issue is that it tries to create a |
|
Sorry @marcorudolphflex there was a bug in that state you tried. Now the test failures are exclusively due to lazy loading. |
No problem. So the issue with lazy loading is the following: In the current state of Now the question is if we want to support So I see 2 alternative solutions:
What do you think? |
|
Edit:
this option just does not work in general. The object would still be a Tidy3DBaseModel after materialization such that it would be not usable. I would prefer the other option which seems more stable. |
|
Yes if the first option works it sounds better, are you going to give it a try? |
|
I can try to do that, but actually it would be better to do that on the pydantic v2 branch as we already observed varied behavior there. |
|
Thanks, sounds good, this is not exactly urgent it's just a nice cleanup. |
7a9f331 to
ca66a4c
Compare
|
Cleaned up and rebased, @yaugenst-flex you wanna approve? |
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
tidy3d/components/base.pyLines 203-217 203 @classmethod
204 def _get_type_value(cls, obj: dict[str, Any]) -> str:
205 """Return the type tag from a raw dictionary."""
206 if not isinstance(obj, dict):
! 207 raise TypeError("Input must be a dict")
208 try:
209 type_value = obj[TYPE_TAG_STR]
! 210 except KeyError as exc:
! 211 raise ValueError(f'Missing "{TYPE_TAG_STR}" in data') from exc
212 if not isinstance(type_value, str) or not type_value:
! 213 raise ValueError(f'Invalid "{TYPE_TAG_STR}" value: {type_value!r}')
214 return type_value
215
216 @classmethod
217 def _get_registered_class(cls, type_value: str) -> type[Tidy3dBaseModel]:Lines 216-225 216 @classmethod
217 def _get_registered_class(cls, type_value: str) -> type[Tidy3dBaseModel]:
218 try:
219 return TYPE_TO_CLASS_MAP[type_value]
! 220 except KeyError as exc:
! 221 raise ValueError(f"Unknown type: {type_value}") from exc
222
223 @classmethod
224 def _should_dispatch_to(cls, target_cls: type[Tidy3dBaseModel]) -> bool:
225 """Return True if ``cls`` allows auto-dispatch to ``target_cls``."""Lines 232-240 232 target_cls = cls._get_registered_class(type_value)
233 if cls._should_dispatch_to(target_cls):
234 return target_cls
235 if target_cls is cls:
! 236 return cls
237 raise ValueError(
238 f'Cannot parse type "{type_value}" using {cls.__name__}; expected subclass of {cls.__name__}.'
239 )Lines 569-577 569 -------
570 >>> simulation = Simulation.from_json(fname='folder/sim.json') # doctest: +SKIP
571 """
572 model_dict = cls.dict_from_json(fname=fname)
! 573 return cls._parse_model_dict(model_dict, **parse_obj_kwargs)
574
575 @classmethod
576 def dict_from_json(cls, fname: PathLike) -> dict:
577 """Load dictionary of the model from a .json file. |
yaugenst-flex
left a comment
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.
LGTM, minor test nit
ca66a4c to
2858b9f
Compare
…eModel.from_file for various components
2858b9f to
3e288b1
Compare
Greptile Overview
Greptile Summary
This PR refactors polymorphic deserialization by centralizing type registry and parsing logic in
Tidy3dBaseModel, enabling automatic type resolution when loading files.Key changes:
TYPE_TO_CLASS_MAPfrom Expression plugin to base class__init_subclass__()parse_obj()override to dispatch to correct subclass based ontypefieldCritical issue found:
tidy3d/components/base.pyhas a logical error that breaks the intended behavior. The condition compares aModelFieldobject to a string, which will always be False, causingparse_obj()to never use the polymorphic dispatch logic.Minor issues:
Confidence Score: 1/5
tidy3d/components/base.pycontains a logic error that compares aModelFieldobject to a string literal "Tidy3dBaseModel", which will always evaluate to False. This means the polymorphicparse_obj()behavior will never be triggered when callingTidy3dBaseModel.parse_obj(), breaking the core feature this PR is intended to add. The fix is simple (change tocls.__name__ == "Tidy3dBaseModel"), but this is a critical functional bug that needs to be resolved before merging.Important Files Changed
File Analysis
TYPE_TO_CLASS_MAPregistry andparse_obj()override with critical bug in line 219 condition checkTidy3dBaseModel.from_file(). Clean refactoring with minor variable naming issueSequence Diagram
sequenceDiagram participant User participant Tidy3dBaseModel participant TYPE_TO_CLASS_MAP participant Subclass as Simulation/HeatSimulation/etc Note over Tidy3dBaseModel,Subclass: Registration Phase (at import time) Subclass->>Tidy3dBaseModel: __init_subclass__() Tidy3dBaseModel->>Tidy3dBaseModel: add_type_field() Tidy3dBaseModel->>Tidy3dBaseModel: generate_docstring() Tidy3dBaseModel->>TYPE_TO_CLASS_MAP: Register type -> class mapping Note over TYPE_TO_CLASS_MAP: {"Simulation": Simulation,<br/>"HeatSimulation": HeatSimulation, ...} Note over User,Subclass: Loading Phase (from_file) User->>Tidy3dBaseModel: from_file("sim.json") Tidy3dBaseModel->>Tidy3dBaseModel: dict_from_file("sim.json") Tidy3dBaseModel->>Tidy3dBaseModel: parse_obj({"type": "HeatSimulation", ...}) Note over Tidy3dBaseModel: BUG: Line 219 condition always False Tidy3dBaseModel->>TYPE_TO_CLASS_MAP: Get class for "HeatSimulation" TYPE_TO_CLASS_MAP-->>Tidy3dBaseModel: HeatSimulation class Tidy3dBaseModel->>Subclass: HeatSimulation(**obj) Subclass-->>User: HeatSimulation instance