Skip to content
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

Merged
merged 1 commit into from
Jan 27, 2025

Conversation

pkerpedjiev
Copy link
Member

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 to deepcopy preserves the class vars of the subclasses.

Here's the error that occurs in the added test without this change:

                    return pydantic_extra[item]
                except KeyError as exc:
>                   raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc
E                   AttributeError: 'PluginTrack' object has no attribute 'plugin_url'

Fixes #___

Checklist

  • Unit tests added or updated
  • Documentation added or updated
  • Updated CHANGELOG.md

@pkerpedjiev pkerpedjiev force-pushed the kerpedji/deepcopy branch 3 times, most recently from 4370b05 to 1aec781 Compare January 25, 2025 17:20
@nvictus nvictus requested a review from manzt January 25, 2025 18:32
@@ -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)
Copy link
Member

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:

Suggested change
copy = deepcopy(model)
copy = model.copy(deep=True)

Copy link
Member Author

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

Copy link
Member

@manzt manzt left a 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.

Comment on lines +212 to +243
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
Copy link
Member

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!

@pkerpedjiev pkerpedjiev merged commit bbf3a01 into main Jan 27, 2025
6 checks passed
@pkerpedjiev pkerpedjiev deleted the kerpedji/deepcopy branch January 27, 2025 17:17
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