-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix edit plugin cancel flow restoring in-memory tags (#6104) #6200
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
base: master
Are you sure you want to change the base?
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.
Hey there - I've reviewed your changes - here's some feedback:
- Double‑check the
apply_data(objs, new_data, old_data)calls used for cancel / continue flows actually restore the original snapshot and don’t accidentally re‑apply edited values (the parameter order and behavior ofapply_dataare critical here). - Given that
importer_editnow returnsAction.RETAGin one branch andNonein the other, verify that all callers correctly handle both return types and that the newNonepath really preserves the expected import state without triggering unintended follow‑up actions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Double‑check the `apply_data(objs, new_data, old_data)` calls used for cancel / continue flows actually restore the original snapshot and don’t accidentally re‑apply edited values (the parameter order and behavior of `apply_data` are critical here).
- Given that `importer_edit` now returns `Action.RETAG` in one branch and `None` in the other, verify that all callers correctly handle both return types and that the new `None` path really preserves the expected import state without triggering unintended follow‑up actions.
## Individual Comments
### Comment 1
<location> `beetsplug/edit.py:382-389` </location>
<code_context>
def importer_edit(self, session, task):
"""Callback for invoking the functionality during an interactive
import session on the *original* item tags.
"""
# Assign negative temporary ids to Items that are not in the database
# yet. By using negative values, no clash with items in the database
# can occur.
for i, obj in enumerate(task.items, start=1):
# The importer may set the id to None when re-importing albums.
if not obj._db or obj.id is None:
obj.id = -i
# Present the YAML to the user and let them change it.
fields = self._get_fields(album=False, extra=[])
success = self.edit_objects(task.items, fields)
# Remove temporary ids.
for obj in task.items:
if obj.id < 0:
obj.id = None
# Save the new data.
if success:
# Return Action.RETAG, which makes the importer write the tags
# to the files if needed without re-applying metadata.
return Action.RETAG
else:
# Edit cancelled / no edits made. `edit_objects` has already
# restored each object to its original in-memory state, so there
# is nothing more to do here. Returning None lets the importer
# resume the candidate prompt.
return None
</code_context>
<issue_to_address>
**suggestion (code-quality):** Replace if statement with if expression ([`assign-if-exp`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/assign-if-exp/))
```suggestion
return Action.RETAG if success else None
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| # to the files if needed without re-applying metadata. | ||
| return Action.RETAG | ||
| else: | ||
| # Edit cancelled / no edits made. Revert changes. | ||
| for obj in task.items: | ||
| obj.read() | ||
| # Edit cancelled / no edits made. `edit_objects` has already | ||
| # restored each object to its original in-memory state, so there | ||
| # is nothing more to do here. Returning None lets the importer | ||
| # resume the candidate prompt. | ||
| return None |
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.
suggestion (code-quality): Replace if statement with if expression (assign-if-exp)
| # to the files if needed without re-applying metadata. | |
| return Action.RETAG | |
| else: | |
| # Edit cancelled / no edits made. Revert changes. | |
| for obj in task.items: | |
| obj.read() | |
| # Edit cancelled / no edits made. `edit_objects` has already | |
| # restored each object to its original in-memory state, so there | |
| # is nothing more to do here. Returning None lets the importer | |
| # resume the candidate prompt. | |
| return None | |
| return Action.RETAG if success else None |
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 fixes a bug where temporary tags injected by the fromfilename plugin (and potentially other plugins) were silently discarded when cancelling edit sessions during interactive imports. The fix ensures that the edit plugin restores objects to their original in-memory state rather than reloading from disk, preserving plugin-provided metadata across cancelled edit sessions.
Key Changes:
- Modified the cancel and "continue Editing" flows to restore the original in-memory state using
apply_data(objs, new_data, old_data) - Updated
importer_editto returnNoneinstead of re-reading from disk when edits are cancelled, allowing the importer to resume with preserved metadata
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| docs/changelog.rst | Documents the bug fix for preserving temporary tags from fromfilename plugin during cancelled edit sessions |
| beetsplug/edit.py | Implements the fix by reverting to original in-memory state on cancel/continue-editing, and removes the disk reload logic from importer_edit |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #6200 +/- ##
==========================================
+ Coverage 67.93% 67.96% +0.02%
==========================================
Files 137 137
Lines 18677 18674 -3
Branches 3155 3152 -3
==========================================
+ Hits 12688 12691 +3
+ Misses 5324 5318 -6
Partials 665 665
🚀 New features to boost your workflow:
|
Description
Fixes #6104.
When using the
fromfilenameandeditplugins together during import, aborted edit sessions could silently discard the temporary tags injected byfromfilename(e.g., track number and title derived from the filename). This happened when usingeDitoredit Candidatesand then cancelling: the edit plugin reverted objects by re-reading from disk, which does not contain thefromfilename-generated metadata.This PR changes the
editplugin so that cancel and “continue Editing” both roll back objects to the original in-memory snapshot captured before opening the editor, instead of reloading from the files. This preserves temporary tags provided by other plugins (likefromfilename) across aborted edit sessions, while still only writing to disk when the user chooses Apply.importer_editis also updated to rely on this rollback behavior when edits are cancelled, rather than re-reading from disk, so interactive imports resume with the same in-memory metadata they started with.To Do
editplugin tests exercised and extended to cover the updated cancel/rollback behavior.)