Skip to content

Commit 86c1c56

Browse files
committed
refactor util + other misc improvements
1 parent 68c3db0 commit 86c1c56

File tree

16 files changed

+1392
-1140
lines changed

16 files changed

+1392
-1140
lines changed

beets/__init__.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@
1313
# included in all copies or substantial portions of the Software.
1414

1515

16-
from sys import stderr
17-
18-
import confuse
19-
16+
from .config import IncludeLazyConfig, config
2017
from .util import deprecate_imports
2118

22-
__version__ = "2.5.1"
23-
__author__ = "Adrian Sampson <[email protected]>"
19+
__version__: str = "2.5.1"
20+
__author__: str = "Adrian Sampson <[email protected]>"
21+
22+
__all__: list[str] = ["IncludeLazyConfig", "config"]
2423

2524

26-
def __getattr__(name: str):
25+
def __getattr__(name: str) -> str:
2726
"""Handle deprecated imports."""
2827
return deprecate_imports(
2928
old_module=__name__,
@@ -34,23 +33,3 @@ def __getattr__(name: str):
3433
name=name,
3534
version="3.0.0",
3635
)
37-
38-
39-
class IncludeLazyConfig(confuse.LazyConfig):
40-
"""A version of Confuse's LazyConfig that also merges in data from
41-
YAML files specified in an `include` setting.
42-
"""
43-
44-
def read(self, user=True, defaults=True):
45-
super().read(user, defaults)
46-
47-
try:
48-
for view in self["include"]:
49-
self.set_file(view.as_filename())
50-
except confuse.NotFoundError:
51-
pass
52-
except confuse.ConfigReadError as err:
53-
stderr.write(f"configuration `import` failed: {err.reason}")
54-
55-
56-
config = IncludeLazyConfig("beets", __name__)

beets/autotag/distance.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from unidecode import unidecode
1010

1111
from beets import config, metadata_plugins
12-
from beets.util import as_string, cached_classproperty, get_most_common_tags
12+
from beets.util import as_string, cached_classproperty
13+
from beets.util.misc import get_most_common_tags
1314

1415
if TYPE_CHECKING:
1516
from collections.abc import Iterator, Sequence
@@ -345,7 +346,7 @@ def add_string(self, key: str, str1: str | None, str2: str | None):
345346
dist = string_dist(str1, str2)
346347
self.add(key, dist)
347348

348-
def add_data_source(self, before: str | None, after: str | None) -> None:
349+
def add_data_source(self, before: object, after: str | None) -> None:
349350
if before != after and (
350351
before or len(metadata_plugins.find_metadata_source_plugins()) > 1
351352
):
@@ -384,11 +385,19 @@ def track_distance(
384385
cached because this function is called many times during the matching
385386
process and their access comes with a performance overhead.
386387
"""
387-
dist = Distance()
388+
dist: Distance = Distance()
388389

389390
# Length.
391+
info_length: float | None
390392
if info_length := track_info.length:
391-
diff = abs(item.length - info_length) - get_track_length_grace()
393+
diff: float = (
394+
abs(
395+
(item.length - info_length)
396+
if isinstance(item.length, (int, float))
397+
else 0
398+
)
399+
- get_track_length_grace()
400+
)
392401
dist.add_ratio("track_length", diff, get_track_length_max())
393402

394403
# Title.

beets/autotag/match.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from beets import config, logging, metadata_plugins
2828
from beets.autotag import AlbumInfo, AlbumMatch, TrackInfo, TrackMatch, hooks
29-
from beets.util import get_most_common_tags
29+
from beets.util.misc import get_most_common_tags
3030

3131
from .distance import VA_ARTISTS, distance, track_distance
3232

beets/config.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from sys import stderr
2+
3+
import confuse
4+
from typing_extensions import override
5+
6+
7+
class IncludeLazyConfig(confuse.LazyConfig):
8+
"""A version of Confuse's LazyConfig that also merges in data from
9+
YAML files specified in an `include` setting.
10+
"""
11+
12+
@override
13+
def read(self, user: bool = True, defaults: bool = True) -> None:
14+
super().read(user, defaults)
15+
16+
try:
17+
view: confuse.Subview
18+
for view in self["include"]:
19+
self.set_file(view.as_filename())
20+
except confuse.NotFoundError:
21+
pass
22+
except confuse.ConfigReadError as err:
23+
_ = stderr.write(f"configuration `import` failed: {err.reason}")
24+
25+
26+
config: IncludeLazyConfig = IncludeLazyConfig("beets", __name__)

beets/logging.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
RootLogger,
3838
StreamHandler,
3939
)
40-
from typing import TYPE_CHECKING, Any, Mapping, TypeVar, Union, overload
40+
from typing import TYPE_CHECKING, Any, TypeVar, Union, overload
4141

4242
__all__ = [
4343
"DEBUG",
@@ -54,9 +54,11 @@
5454
]
5555

5656
if TYPE_CHECKING:
57-
T = TypeVar("T")
57+
from collections.abc import Mapping
5858
from types import TracebackType
5959

60+
T = TypeVar("T")
61+
6062
# see https://github.com/python/typeshed/blob/main/stdlib/logging/__init__.pyi
6163
_SysExcInfoType = Union[
6264
tuple[type[BaseException], BaseException, Union[TracebackType, None]],
@@ -144,24 +146,24 @@ def _log(
144146
class ThreadLocalLevelLogger(Logger):
145147
"""A version of `Logger` whose level is thread-local instead of shared."""
146148

147-
def __init__(self, name, level=NOTSET):
148-
self._thread_level = threading.local()
149-
self.default_level = NOTSET
149+
def __init__(self, name: str, level: int = NOTSET) -> None:
150+
self._thread_level: threading.local = threading.local()
151+
self.default_level: int = NOTSET
150152
super().__init__(name, level)
151153

152154
@property
153-
def level(self):
155+
def level(self) -> int:
154156
try:
155157
return self._thread_level.level
156158
except AttributeError:
157159
self._thread_level.level = self.default_level
158160
return self.level
159161

160162
@level.setter
161-
def level(self, value):
163+
def level(self, value: int) -> None:
162164
self._thread_level.level = value
163165

164-
def set_global_level(self, level):
166+
def set_global_level(self, level: int) -> None:
165167
"""Set the level on the current thread + the default value for all
166168
threads.
167169
"""

beets/ui/__init__.py

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929

3030
import confuse
3131

32-
from beets import IncludeLazyConfig, config, library, logging, plugins, util
32+
from beets import library, logging, plugins, util
33+
from beets.config import config
3334
from beets.dbcore import db
3435
from beets.dbcore import query as db_query
35-
from beets.ui import commands, core
3636
from beets.ui._common import UserError
3737
from beets.ui.colors import color_split, colorize, uncolorize
3838
from beets.ui.core import (
@@ -52,11 +52,12 @@
5252
should_write,
5353
show_model_changes,
5454
split_into_lines,
55-
uncolorize,
5655
)
5756

5857
if TYPE_CHECKING:
59-
from beets.library.library import Library
58+
from collections.abc import Callable
59+
60+
from beets.config import IncludeLazyConfig
6061

6162

6263
# On Windows platforms, use colorama to support "ANSI" terminal colors.
@@ -77,8 +78,6 @@
7778
"UserError",
7879
"color_split",
7980
"colorize",
80-
"commands",
81-
"core",
8281
"input_",
8382
"input_options",
8483
"input_select_objects",
@@ -130,7 +129,7 @@ def _configure(options: optparse.Values) -> IncludeLazyConfig:
130129
# special handling lets specified plugins get loaded before we
131130
# finish parsing the command line.
132131
if getattr(options, "config", None) is not None:
133-
overlay_path = options.config
132+
overlay_path: str | None = options.config
134133
del options.config
135134
config.set_file(overlay_path)
136135
else:
@@ -148,7 +147,7 @@ def _configure(options: optparse.Values) -> IncludeLazyConfig:
148147
"overlaying configuration: {}", util.displayable_path(overlay_path)
149148
)
150149

151-
config_path = config.user_config_path()
150+
config_path: str = config.user_config_path()
152151
if os.path.isfile(config_path):
153152
log.debug("user configuration: {}", util.displayable_path(config_path))
154153
else:
@@ -161,10 +160,12 @@ def _configure(options: optparse.Values) -> IncludeLazyConfig:
161160
return config
162161

163162

164-
def _ensure_db_directory_exists(path):
163+
def _ensure_db_directory_exists(
164+
path: util.PathLike,
165+
):
165166
if path == b":memory:": # in memory db
166167
return
167-
newpath = os.path.dirname(path)
168+
newpath: bytes | str = os.path.dirname(path)
168169
if not os.path.isdir(newpath):
169170
if input_yn(
170171
f"The database directory {util.displayable_path(newpath)} does not"
@@ -175,16 +176,17 @@ def _ensure_db_directory_exists(path):
175176

176177
def _open_library(config: confuse.LazyConfig) -> library.Library:
177178
"""Create a new library instance from the configuration."""
178-
dbpath = util.bytestring_path(config["library"].as_filename())
179+
dbpath: bytes = util.bytestring_path(config["library"].as_filename())
179180
_ensure_db_directory_exists(dbpath)
181+
lib: library.Library
180182
try:
181183
lib = library.Library(
182184
dbpath,
183185
config["directory"].as_filename(),
184186
get_path_formats(),
185187
get_replacements(),
186188
)
187-
lib.get_item(0) # Test database connection.
189+
_ = lib.get_item(0) # Test database connection.
188190
except (sqlite3.OperationalError, sqlite3.DatabaseError) as db_error:
189191
log.debug("{}", traceback.format_exc())
190192
raise UserError(
@@ -199,11 +201,11 @@ def _open_library(config: confuse.LazyConfig) -> library.Library:
199201
return lib
200202

201203

202-
def _raw_main(args: list[str], lib: Library | None = None) -> None:
204+
def _raw_main(args: list[str], lib: library.Library | None = None) -> None:
203205
"""A helper function for `main` without top-level exception
204206
handling.
205207
"""
206-
parser = SubcommandsOptionParser()
208+
parser: SubcommandsOptionParser = SubcommandsOptionParser()
207209
parser.add_format_option(flags=("--format-item",), target=library.Item)
208210
parser.add_format_option(flags=("--format-album",), target=library.Album)
209211
_ = parser.add_option(
@@ -227,7 +229,10 @@ def _raw_main(args: list[str], lib: Library | None = None) -> None:
227229
)
228230

229231
def parse_csl_callback(
230-
option: optparse.Option, _, value: str, parser: SubcommandsOptionParser
232+
option: optparse.Option,
233+
_: Callable[..., object],
234+
value: str,
235+
parser: SubcommandsOptionParser,
231236
) -> None:
232237
"""Parse a comma-separated list of values."""
233238
setattr(
@@ -236,36 +241,38 @@ def parse_csl_callback(
236241
list(filter(None, value.split(","))),
237242
)
238243

239-
parser.add_option(
244+
_ = parser.add_option(
240245
"-p",
241246
"--plugins",
242247
dest="plugins",
243248
action="callback",
244249
callback=parse_csl_callback,
245250
help="a comma-separated list of plugins to load",
246251
)
247-
parser.add_option(
252+
_ = parser.add_option(
248253
"-P",
249254
"--disable-plugins",
250255
dest="disabled_plugins",
251256
action="callback",
252257
callback=parse_csl_callback,
253258
help="a comma-separated list of plugins to disable",
254259
)
255-
parser.add_option(
260+
_ = parser.add_option(
256261
"-h",
257262
"--help",
258263
dest="help",
259264
action="store_true",
260265
help="show this help message and exit",
261266
)
262-
parser.add_option(
267+
_ = parser.add_option(
263268
"--version",
264269
dest="version",
265270
action="store_true",
266271
help=optparse.SUPPRESS_HELP,
267272
)
268273

274+
options: optparse.Values
275+
subargs: list[str]
269276
options, subargs = parser.parse_global_options(args)
270277

271278
# Special case for the `config --edit` command: bypass _setup so
@@ -280,14 +287,17 @@ def parse_csl_callback(
280287

281288
return config_edit()
282289

283-
test_lib = bool(lib)
290+
test_lib: bool = bool(lib)
291+
subcommands: list[Subcommand]
284292
subcommands, lib = _setup(options, lib)
285293
parser.add_subcommand(*subcommands)
286294

295+
subcommand: Subcommand
296+
suboptions: optparse.Values
287297
subcommand, suboptions, subargs = parser.parse_subcommand(subargs)
288298
subcommand.func(lib, suboptions, subargs)
289299

290-
plugins.send("cli_exit", lib=lib)
300+
_ = plugins.send("cli_exit", lib=lib)
291301
if not test_lib:
292302
# Clean up the library unless it came from the test harness.
293303
lib._close()
@@ -308,7 +318,7 @@ def main(args: list[str] | None = None) -> None:
308318
try:
309319
_raw_main(args or [])
310320
except UserError as exc:
311-
message = exc.args[0] if exc.args else None
321+
message: object = exc.args[0] if exc.args else None
312322
log.error("error: {}", message)
313323
sys.exit(1)
314324
except util.HumanReadableError as exc:
@@ -329,7 +339,7 @@ def main(args: list[str] | None = None) -> None:
329339
except OSError as exc:
330340
if exc.errno == errno.EPIPE:
331341
# "Broken pipe". End silently.
332-
sys.stderr.close()
342+
_ = sys.stderr.close()
333343
else:
334344
raise
335345
except KeyboardInterrupt:

0 commit comments

Comments
 (0)