-
Notifications
You must be signed in to change notification settings - Fork 12
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
fix: Use deepcopy instead of model_dump to copy view #163
Conversation
4370b05
to
1aec781
Compare
src/higlass/_utils.py
Outdated
@@ -75,7 +76,7 @@ def ensure_list(x: T | list[T] | None) -> list[T]: | |||
|
|||
def copy_unique(model: ModelT) -> ModelT: | |||
"""Creates a deep copy of a pydantic BaseModel with new UID.""" | |||
copy = model.__class__(**model.model_dump()) | |||
copy = deepcopy(model) |
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.
Not familiar enough to know the consequences of deepcopy
on a pydantic model, but pydantic appears to have this API:
copy = deepcopy(model) | |
copy = model.copy(deep=True) |
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.
So that works too. I don't know what the difference is either but I switched over to model.model_copy (deep=True)
(model.copy
is deprecated).
1aec781
to
95d3c3f
Compare
95d3c3f
to
350e134
Compare
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.
Looks good to me! I'm surprised by the hack I had there before (rather than using .copy(deep=True)
. This is certainly an improvement.
def test_plugin_track(): | ||
"""Test that plugin track attributes are maintained after a copy.""" | ||
some_url = "https://some_url" | ||
|
||
# Here we'll create a custom plugin track. | ||
class PileupTrack(hg.PluginTrack): | ||
type: Literal["pileup"] = "pileup" | ||
plugin_url: ClassVar[str] = some_url | ||
|
||
# Specify the track-specific data | ||
pileup_data = { | ||
"type": "bam", | ||
"url": "https://some_url/sorted.bam", | ||
"chromSizesUrl": "https://some_url/sorted.chromsizes.bam", | ||
"options": {"maxTileWidth": 30000}, | ||
} | ||
|
||
# Create and use the custom track | ||
pileup_track = PileupTrack(data=pileup_data) | ||
|
||
view = hg.view((pileup_track, "top")) | ||
uid1 = view.uid | ||
assert view.tracks.top[0].plugin_url == some_url | ||
|
||
# The .domain() function creates a copy of the view. We want to make sure | ||
# that the plugin_url attribute of the PluginTrack is maintained | ||
view = view.domain(x=[0, 10]) | ||
uid2 = view.uid | ||
assert view.tracks.top[0].plugin_url == some_url | ||
|
||
# Check to make sure the copy behavior changed the uid as expected | ||
assert uid1 != uid2 |
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.
Thanks for adding a test!
Description
Calling the
.domain()
function on a view creates a copy of the view. The current copy method dumps the view model and creates a new one from the view. Because views expect PluginTrack types instead of specific subclasses, the dumped model does not contain the class vars of the subclasses (e.g. PileupTrack). Switching todeepcopy
preserves the class vars of the subclasses.Here's the error that occurs in the added test without this change:
Fixes #___
Checklist