-
Notifications
You must be signed in to change notification settings - Fork 2k
Refactored beets/random.py and moved into beetsplug/random.py #5924
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
This comment was marked as resolved.
This comment was marked as resolved.
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 moves the beets/random.py module into beetsplug/random.py to consolidate random functionality with its only consumer, the random plugin, thereby keeping core modules cleaner.
- Moved all functions from
beets/random.pyintobeetsplug/random.py - Added comprehensive type hints for better code clarity and tooling support
- Enhanced test coverage with additional test cases for edge cases and different functionality modes
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| beets/random.py | Complete removal of the standalone random module |
| beetsplug/random.py | Integration of moved functions with added type hints and improved implementation |
| test/plugins/test_random.py | Updated import path and added comprehensive test cases for better coverage |
daf1136 to
3941fd3
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5924 +/- ##
==========================================
+ Coverage 68.68% 68.76% +0.07%
==========================================
Files 138 138
Lines 18532 18572 +40
Branches 3061 3067 +6
==========================================
+ Hits 12729 12771 +42
+ Misses 5149 5147 -2
Partials 654 654
🚀 New features to boost your workflow:
|
snejus
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.
A couple of more comments. I found that unused default arguments are still present, so I unresolved my previous two comments where I commented about them.
snejus
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.
Since you've added field argument, I added a couple of related comments. This should be mentioned in the changelog as well.
039f18f to
029f1dd
Compare
replaced them with `field`. Removed unnecessary default parameters where applicable.
snejus
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.
I gave this a test and found two bugs:
- List fields are not handled
$ beet random --field artists -e
Traceback (most recent call last):
File "/home/sarunas/.local/bin/beet", line 8, in <module>
main()
File "/home/sarunas/repo/beets/beets/ui/__init__.py", line 1629, in main
_raw_main(args)
File "/home/sarunas/repo/beets/beets/ui/__init__.py", line 1608, in _raw_main
subcommand.func(lib, suboptions, subargs)
File "/home/sarunas/repo/beets/beetsplug/random.py", line 39, in random_func
for obj in random_objs(
File "/home/sarunas/repo/beets/beetsplug/random.py", line 112, in _equal_chance_permutation
groups[k] = list(values)
TypeError: unhashable type: 'list'```- items aren't sorted before grouping in equal chance permutation. See
beet listoutput:
$ beet list path::aaa title- -f '$album: $artist - $title'
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - Triple Masala
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - Machete
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - Lost in Time
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - Freaks at Work
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - der killer wahl 185bpm
RAVE SLUTZ: Fallen Shrine & dj Christian NXC - DEEEJAAAY
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - Blah
Netikras albumas: Karpiz - AAAvs
$ beet random path::aaa title- -f '$album: $artist - $title' --field album -e -n10
RAVE SLUTZ: Fallen Shrine & dj Christian NXC - DEEEJAAAY
Netikras albumas: Karpiz - AAA
BLAH!!!! malakaa!! todotiernos de mierdaaa: DER SANDMANN - BlahI expected to see the same items but in random order.
I'm keen to finally wrap this up, so feel free to just paste this fix:
diff --git a/beetsplug/random.py b/beetsplug/random.py
index 9714c8e53..aa65dd5d5 100644
--- a/beetsplug/random.py
+++ b/beetsplug/random.py
@@ -19,4 +19,4 @@
from itertools import groupby, islice
-from operator import attrgetter
-from typing import TYPE_CHECKING, Any
+from operator import methodcaller
+from typing import TYPE_CHECKING
@@ -86,5 +86,2 @@ def commands
-NOT_FOUND_SENTINEL = object()
-
-
def _equal_chance_permutation(
@@ -97,22 +94,12 @@ def _equal_chance_permutation
# Group the objects by field so we can sample from them.
- key = attrgetter(field)
-
- def get_attr(obj: LibModel) -> Any:
- try:
- return key(obj)
- except AttributeError:
- return NOT_FOUND_SENTINEL
-
- sorted(objs, key=get_attr)
-
- groups: dict[str | object, list[LibModel]] = {
- NOT_FOUND_SENTINEL: [],
- }
- for k, values in groupby(objs, key=get_attr):
- groups[k] = list(values)
- # shuffle in category
- random.shuffle(groups[k])
-
- # Remove items without the field value.
- del groups[NOT_FOUND_SENTINEL]
+ get_attr = methodcaller("get", field)
+
+ groups = {}
+ for k, values in groupby(sorted(objs, key=get_attr), key=get_attr):
+ if k is not None:
+ vals = list(values)
+ # shuffle in category
+ random.shuffle(vals)
+ groups[str(k)] = vals
+
while groups:| key = attrgetter(field) | ||
|
|
||
| def get_attr(obj: LibModel) -> Any: | ||
| try: | ||
| return key(obj) | ||
| except AttributeError: | ||
| return NOT_FOUND_SENTINEL | ||
|
|
||
| sorted(objs, key=get_attr) | ||
|
|
||
| groups: dict[str | object, list[LibModel]] = { | ||
| NOT_FOUND_SENTINEL: [], | ||
| } |
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.
LibModel is a dictionary - so this can be simplified with using LibModel.get instead of try/except + sentinel.
| key = attrgetter(field) | |
| def get_attr(obj: LibModel) -> Any: | |
| try: | |
| return key(obj) | |
| except AttributeError: | |
| return NOT_FOUND_SENTINEL | |
| sorted(objs, key=get_attr) | |
| groups: dict[str | object, list[LibModel]] = { | |
| NOT_FOUND_SENTINEL: [], | |
| } | |
| # Group the objects by field so we can sample from them. | |
| get_attr = methodcaller("get", field) | |
| sorted(objs, key=get_attr) | |
| groups = {} |
| except AttributeError: | ||
| return NOT_FOUND_SENTINEL | ||
|
|
||
| sorted(objs, key=get_attr) |
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.
This has no effect - you want to either move it to
for k, values in groupby(sorted(objs, key=get_attr), key=get_attr):or better: revert to what we had previously by casting entities to list and send list[LibModel] through:
diff --git a/beetsplug/random.py b/beetsplug/random.py
index 9714c8e53..44adc9fbe 100644
--- a/beetsplug/random.py
+++ b/beetsplug/random.py
@@ -33,7 +33,7 @@
def random_func(lib: Library, opts: optparse.Values, args: list[str]):
"""Select some random items or albums and print the results."""
# Fetch all the objects matching the query into a list.
- objs = lib.albums(args) if opts.album else lib.items(args)
+ objs = list(lib.albums(args) if opts.album else lib.items(args))
# Print a random subset.
for obj in random_objs(which allows
| sorted(objs, key=get_attr) | |
| objs.sort(key=get_attr) |
| groups[k] = list(values) | ||
| # shuffle in category | ||
| random.shuffle(groups[k]) | ||
|
|
||
| # Remove items without the field value. | ||
| del groups[NOT_FOUND_SENTINEL] |
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.
- Need to handle list fields like
artists,albumtypes- we can just cast the keys tostr - Skip missing (
None) values removing the need to delete them later
| groups[k] = list(values) | |
| # shuffle in category | |
| random.shuffle(groups[k]) | |
| # Remove items without the field value. | |
| del groups[NOT_FOUND_SENTINEL] | |
| if k is not None: | |
| vals = list(values) | |
| # shuffle in category | |
| random.shuffle(vals) | |
| groups[str(k)] = vals | |
The
beets/random.pymodule was only used by the random plugin, so I moved its functions intobeetsplug/random.pyto keep core modules cleaner.Changes: