mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Replace percent formatting
This commit is contained in:
parent
4a361bd501
commit
9352a79e41
25 changed files with 70 additions and 93 deletions
|
|
@ -78,9 +78,9 @@ def string_dist(str1: str | None, str2: str | None) -> float:
|
||||||
# example, "the something" should be considered equal to
|
# example, "the something" should be considered equal to
|
||||||
# "something, the".
|
# "something, the".
|
||||||
for word in SD_END_WORDS:
|
for word in SD_END_WORDS:
|
||||||
if str1.endswith(", %s" % word):
|
if str1.endswith(f", {word}"):
|
||||||
str1 = f"{word} {str1[: -len(word) - 2]}"
|
str1 = f"{word} {str1[: -len(word) - 2]}"
|
||||||
if str2.endswith(", %s" % word):
|
if str2.endswith(f", {word}"):
|
||||||
str2 = f"{word} {str2[: -len(word) - 2]}"
|
str2 = f"{word} {str2[: -len(word) - 2]}"
|
||||||
|
|
||||||
# Perform a couple of basic normalizing substitutions.
|
# Perform a couple of basic normalizing substitutions.
|
||||||
|
|
@ -444,7 +444,7 @@ def distance(
|
||||||
# Preferred media options.
|
# Preferred media options.
|
||||||
media_patterns: Sequence[str] = preferred_config["media"].as_str_seq()
|
media_patterns: Sequence[str] = preferred_config["media"].as_str_seq()
|
||||||
options = [
|
options = [
|
||||||
re.compile(r"(\d+x)?(%s)" % pat, re.I) for pat in media_patterns
|
re.compile(rf"(\d+x)?({pat})", re.I) for pat in media_patterns
|
||||||
]
|
]
|
||||||
if options:
|
if options:
|
||||||
dist.add_priority("media", album_info.media, options)
|
dist.add_priority("media", album_info.media, options)
|
||||||
|
|
|
||||||
|
|
@ -1158,7 +1158,7 @@ class Database:
|
||||||
"""
|
"""
|
||||||
# Get current schema.
|
# Get current schema.
|
||||||
with self.transaction() as tx:
|
with self.transaction() as tx:
|
||||||
rows = tx.query("PRAGMA table_info(%s)" % table)
|
rows = tx.query(f"PRAGMA table_info({table})")
|
||||||
current_fields = {row[1] for row in rows}
|
current_fields = {row[1] for row in rows}
|
||||||
|
|
||||||
field_names = set(fields.keys())
|
field_names = set(fields.keys())
|
||||||
|
|
|
||||||
|
|
@ -482,7 +482,7 @@ class Album(LibModel):
|
||||||
"""
|
"""
|
||||||
item = self.items().get()
|
item = self.items().get()
|
||||||
if not item:
|
if not item:
|
||||||
raise ValueError("empty album for album id %d" % self.id)
|
raise ValueError(f"empty album for album id {self.id}")
|
||||||
return os.path.dirname(item.path)
|
return os.path.dirname(item.path)
|
||||||
|
|
||||||
def _albumtotal(self):
|
def _albumtotal(self):
|
||||||
|
|
|
||||||
|
|
@ -831,8 +831,8 @@ class AutotagStub:
|
||||||
|
|
||||||
def _make_track_match(self, artist, album, number):
|
def _make_track_match(self, artist, album, number):
|
||||||
return TrackInfo(
|
return TrackInfo(
|
||||||
title="Applied Track %d" % number,
|
title=f"Applied Track {number}",
|
||||||
track_id="match %d" % number,
|
track_id=f"match {number}",
|
||||||
artist=artist,
|
artist=artist,
|
||||||
length=1,
|
length=1,
|
||||||
index=0,
|
index=0,
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ def input_options(
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
# The first option is the default; mark it.
|
# The first option is the default; mark it.
|
||||||
show_letter = "[%s]" % found_letter.upper()
|
show_letter = f"[{found_letter.upper()}]"
|
||||||
is_default = True
|
is_default = True
|
||||||
else:
|
else:
|
||||||
show_letter = found_letter.upper()
|
show_letter = found_letter.upper()
|
||||||
|
|
@ -308,9 +308,9 @@ def input_options(
|
||||||
if isinstance(default, int):
|
if isinstance(default, int):
|
||||||
default_name = str(default)
|
default_name = str(default)
|
||||||
default_name = colorize("action_default", default_name)
|
default_name = colorize("action_default", default_name)
|
||||||
tmpl = "# selection (default %s)"
|
tmpl = "# selection (default {})"
|
||||||
prompt_parts.append(tmpl % default_name)
|
prompt_parts.append(tmpl.format(default_name))
|
||||||
prompt_part_lengths.append(len(tmpl % str(default)))
|
prompt_part_lengths.append(len(tmpl) - 2 + len(str(default)))
|
||||||
else:
|
else:
|
||||||
prompt_parts.append("# selection")
|
prompt_parts.append("# selection")
|
||||||
prompt_part_lengths.append(len(prompt_parts[-1]))
|
prompt_part_lengths.append(len(prompt_parts[-1]))
|
||||||
|
|
@ -349,7 +349,7 @@ def input_options(
|
||||||
if not fallback_prompt:
|
if not fallback_prompt:
|
||||||
fallback_prompt = "Enter one of "
|
fallback_prompt = "Enter one of "
|
||||||
if numrange:
|
if numrange:
|
||||||
fallback_prompt += "%i-%i, " % numrange
|
fallback_prompt += "{}-{}, ".format(*numrange)
|
||||||
fallback_prompt += ", ".join(display_letters) + ":"
|
fallback_prompt += ", ".join(display_letters) + ":"
|
||||||
|
|
||||||
resp = input_(prompt)
|
resp = input_(prompt)
|
||||||
|
|
@ -406,7 +406,7 @@ def input_select_objects(prompt, objs, rep, prompt_all=None):
|
||||||
objects individually.
|
objects individually.
|
||||||
"""
|
"""
|
||||||
choice = input_options(
|
choice = input_options(
|
||||||
("y", "n", "s"), False, "%s? (Yes/no/select)" % (prompt_all or prompt)
|
("y", "n", "s"), False, f"{prompt_all or prompt}? (Yes/no/select)"
|
||||||
)
|
)
|
||||||
print() # Blank line.
|
print() # Blank line.
|
||||||
|
|
||||||
|
|
@ -420,7 +420,7 @@ def input_select_objects(prompt, objs, rep, prompt_all=None):
|
||||||
answer = input_options(
|
answer = input_options(
|
||||||
("y", "n", "q"),
|
("y", "n", "q"),
|
||||||
True,
|
True,
|
||||||
"%s? (yes/no/quit)" % prompt,
|
f"{prompt}? (yes/no/quit)",
|
||||||
"Enter Y or N:",
|
"Enter Y or N:",
|
||||||
)
|
)
|
||||||
if answer == "y":
|
if answer == "y":
|
||||||
|
|
@ -534,7 +534,7 @@ def _colorize(color, text):
|
||||||
# over all "ANSI codes" in `color`.
|
# over all "ANSI codes" in `color`.
|
||||||
escape = ""
|
escape = ""
|
||||||
for code in color:
|
for code in color:
|
||||||
escape = escape + COLOR_ESCAPE + "%im" % ANSI_CODES[code]
|
escape = escape + COLOR_ESCAPE + f"{ANSI_CODES[code]}m"
|
||||||
return escape + text + RESET_COLOR
|
return escape + text + RESET_COLOR
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1475,7 +1475,7 @@ class SubcommandsOptionParser(CommonOptionsParser):
|
||||||
for subcommand in subcommands:
|
for subcommand in subcommands:
|
||||||
name = subcommand.name
|
name = subcommand.name
|
||||||
if subcommand.aliases:
|
if subcommand.aliases:
|
||||||
name += " (%s)" % ", ".join(subcommand.aliases)
|
name += f" ({', '.join(subcommand.aliases)})"
|
||||||
disp_names.append(name)
|
disp_names.append(name)
|
||||||
|
|
||||||
# Set the help position based on the max width.
|
# Set the help position based on the max width.
|
||||||
|
|
@ -1488,26 +1488,18 @@ class SubcommandsOptionParser(CommonOptionsParser):
|
||||||
# Lifted directly from optparse.py.
|
# Lifted directly from optparse.py.
|
||||||
name_width = help_position - formatter.current_indent - 2
|
name_width = help_position - formatter.current_indent - 2
|
||||||
if len(name) > name_width:
|
if len(name) > name_width:
|
||||||
name = "%*s%s\n" % (formatter.current_indent, "", name)
|
name = f"{' ' * formatter.current_indent}{name}\n"
|
||||||
indent_first = help_position
|
indent_first = help_position
|
||||||
else:
|
else:
|
||||||
name = "%*s%-*s " % (
|
name = f"{' ' * formatter.current_indent}{name:<{name_width}}\n"
|
||||||
formatter.current_indent,
|
|
||||||
"",
|
|
||||||
name_width,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
indent_first = 0
|
indent_first = 0
|
||||||
result.append(name)
|
result.append(name)
|
||||||
help_width = formatter.width - help_position
|
help_width = formatter.width - help_position
|
||||||
help_lines = textwrap.wrap(subcommand.help, help_width)
|
help_lines = textwrap.wrap(subcommand.help, help_width)
|
||||||
help_line = help_lines[0] if help_lines else ""
|
help_line = help_lines[0] if help_lines else ""
|
||||||
result.append("%*s%s\n" % (indent_first, "", help_line))
|
result.append(f"{' ' * indent_first}{help_line}\n")
|
||||||
result.extend(
|
result.extend(
|
||||||
[
|
[f"{' ' * help_position}{line}\n" for line in help_lines[1:]]
|
||||||
"%*s%s\n" % (help_position, "", line)
|
|
||||||
for line in help_lines[1:]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
formatter.dedent()
|
formatter.dedent()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,13 +144,13 @@ def fields_func(lib, opts, args):
|
||||||
|
|
||||||
with lib.transaction() as tx:
|
with lib.transaction() as tx:
|
||||||
# The SQL uses the DISTINCT to get unique values from the query
|
# The SQL uses the DISTINCT to get unique values from the query
|
||||||
unique_fields = "SELECT DISTINCT key FROM (%s)"
|
unique_fields = "SELECT DISTINCT key FROM ({})"
|
||||||
|
|
||||||
print_("Item flexible attributes:")
|
print_("Item flexible attributes:")
|
||||||
_print_keys(tx.query(unique_fields % library.Item._flex_table))
|
_print_keys(tx.query(unique_fields.format(library.Item._flex_table)))
|
||||||
|
|
||||||
print_("Album flexible attributes:")
|
print_("Album flexible attributes:")
|
||||||
_print_keys(tx.query(unique_fields % library.Album._flex_table))
|
_print_keys(tx.query(unique_fields.format(library.Album._flex_table)))
|
||||||
|
|
||||||
|
|
||||||
fields_cmd = ui.Subcommand(
|
fields_cmd = ui.Subcommand(
|
||||||
|
|
@ -1926,7 +1926,7 @@ default_commands.append(stats_cmd)
|
||||||
|
|
||||||
|
|
||||||
def show_version(lib, opts, args):
|
def show_version(lib, opts, args):
|
||||||
print_("beets version %s" % beets.__version__)
|
print_(f"beets version {beets.__version__}")
|
||||||
print_(f"Python version {python_version()}")
|
print_(f"Python version {python_version()}")
|
||||||
# Show plugins.
|
# Show plugins.
|
||||||
names = sorted(p.name for p in plugins.find_plugins())
|
names = sorted(p.name for p in plugins.find_plugins())
|
||||||
|
|
@ -1990,7 +1990,7 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm, inherit):
|
||||||
extra = ""
|
extra = ""
|
||||||
|
|
||||||
changed = ui.input_select_objects(
|
changed = ui.input_select_objects(
|
||||||
"Really modify%s" % extra,
|
f"Really modify{extra}",
|
||||||
changed,
|
changed,
|
||||||
lambda o: print_and_modify(o, mods, dels),
|
lambda o: print_and_modify(o, mods, dels),
|
||||||
)
|
)
|
||||||
|
|
@ -2168,7 +2168,7 @@ def move_items(
|
||||||
else:
|
else:
|
||||||
if confirm:
|
if confirm:
|
||||||
objs = ui.input_select_objects(
|
objs = ui.input_select_objects(
|
||||||
"Really %s" % act,
|
f"Really {act}",
|
||||||
objs,
|
objs,
|
||||||
lambda o: show_path_changes(
|
lambda o: show_path_changes(
|
||||||
[(o.path, o.destination(basedir=dest))]
|
[(o.path, o.destination(basedir=dest))]
|
||||||
|
|
@ -2461,22 +2461,18 @@ def completion_script(commands):
|
||||||
yield "_beet() {\n"
|
yield "_beet() {\n"
|
||||||
|
|
||||||
# Command names
|
# Command names
|
||||||
yield " local commands='%s'\n" % " ".join(command_names)
|
yield f" local commands={' '.join(command_names)!r}\n"
|
||||||
yield "\n"
|
yield "\n"
|
||||||
|
|
||||||
# Command aliases
|
# Command aliases
|
||||||
yield " local aliases='%s'\n" % " ".join(aliases.keys())
|
yield f" local aliases={' '.join(aliases.keys())!r}\n"
|
||||||
for alias, cmd in aliases.items():
|
for alias, cmd in aliases.items():
|
||||||
yield f" local alias__{alias.replace('-', '_')}={cmd}\n"
|
yield f" local alias__{alias.replace('-', '_')}={cmd}\n"
|
||||||
yield "\n"
|
yield "\n"
|
||||||
|
|
||||||
# Fields
|
# Fields
|
||||||
yield " fields='%s'\n" % " ".join(
|
fields = library.Item._fields.keys() | library.Album._fields.keys()
|
||||||
set(
|
yield f" fields={' '.join(fields)!r}\n"
|
||||||
list(library.Item._fields.keys())
|
|
||||||
+ list(library.Album._fields.keys())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Command options
|
# Command options
|
||||||
for cmd, opts in options.items():
|
for cmd, opts in options.items():
|
||||||
|
|
|
||||||
|
|
@ -559,7 +559,7 @@ def spawn(coro):
|
||||||
and child coroutines run concurrently.
|
and child coroutines run concurrently.
|
||||||
"""
|
"""
|
||||||
if not isinstance(coro, types.GeneratorType):
|
if not isinstance(coro, types.GeneratorType):
|
||||||
raise ValueError("%s is not a coroutine" % coro)
|
raise ValueError(f"{coro} is not a coroutine")
|
||||||
return SpawnEvent(coro)
|
return SpawnEvent(coro)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -569,7 +569,7 @@ def call(coro):
|
||||||
returns a value using end(), then this event returns that value.
|
returns a value using end(), then this event returns that value.
|
||||||
"""
|
"""
|
||||||
if not isinstance(coro, types.GeneratorType):
|
if not isinstance(coro, types.GeneratorType):
|
||||||
raise ValueError("%s is not a coroutine" % coro)
|
raise ValueError(f"{coro} is not a coroutine")
|
||||||
return DelegationEvent(coro)
|
return DelegationEvent(coro)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ class Symbol:
|
||||||
self.original = original
|
self.original = original
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Symbol(%s)" % repr(self.ident)
|
return f"Symbol({self.ident!r})"
|
||||||
|
|
||||||
def evaluate(self, env):
|
def evaluate(self, env):
|
||||||
"""Evaluate the symbol in the environment, returning a Unicode
|
"""Evaluate the symbol in the environment, returning a Unicode
|
||||||
|
|
@ -178,7 +178,7 @@ class Call:
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# Function raised exception! Maybe inlining the name of
|
# Function raised exception! Maybe inlining the name of
|
||||||
# the exception will help debug.
|
# the exception will help debug.
|
||||||
return "<%s>" % str(exc)
|
return f"<{exc}>"
|
||||||
return str(out)
|
return str(out)
|
||||||
else:
|
else:
|
||||||
return self.original
|
return self.original
|
||||||
|
|
@ -224,7 +224,7 @@ class Expression:
|
||||||
self.parts = parts
|
self.parts = parts
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Expression(%s)" % (repr(self.parts))
|
return f"Expression({self.parts!r})"
|
||||||
|
|
||||||
def evaluate(self, env):
|
def evaluate(self, env):
|
||||||
"""Evaluate the entire expression in the environment, returning
|
"""Evaluate the entire expression in the environment, returning
|
||||||
|
|
@ -296,9 +296,6 @@ class Parser:
|
||||||
GROUP_CLOSE,
|
GROUP_CLOSE,
|
||||||
ESCAPE_CHAR,
|
ESCAPE_CHAR,
|
||||||
)
|
)
|
||||||
special_char_re = re.compile(
|
|
||||||
r"[%s]|\Z" % "".join(re.escape(c) for c in special_chars)
|
|
||||||
)
|
|
||||||
escapable_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_CLOSE, ARG_SEP)
|
escapable_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_CLOSE, ARG_SEP)
|
||||||
terminator_chars = (GROUP_CLOSE,)
|
terminator_chars = (GROUP_CLOSE,)
|
||||||
|
|
||||||
|
|
@ -310,16 +307,10 @@ class Parser:
|
||||||
"""
|
"""
|
||||||
# Append comma (ARG_SEP) to the list of special characters only when
|
# Append comma (ARG_SEP) to the list of special characters only when
|
||||||
# parsing function arguments.
|
# parsing function arguments.
|
||||||
extra_special_chars = ()
|
extra_special_chars = (ARG_SEP,) if self.in_argument else ()
|
||||||
special_char_re = self.special_char_re
|
special_chars = (*self.special_chars, *extra_special_chars)
|
||||||
if self.in_argument:
|
|
||||||
extra_special_chars = (ARG_SEP,)
|
|
||||||
special_char_re = re.compile(
|
special_char_re = re.compile(
|
||||||
r"[%s]|\Z"
|
rf"[{''.join(map(re.escape, special_chars))}]|\Z"
|
||||||
% "".join(
|
|
||||||
re.escape(c)
|
|
||||||
for c in self.special_chars + extra_special_chars
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
text_parts = []
|
text_parts = []
|
||||||
|
|
@ -327,7 +318,7 @@ class Parser:
|
||||||
while self.pos < len(self.string):
|
while self.pos < len(self.string):
|
||||||
char = self.string[self.pos]
|
char = self.string[self.pos]
|
||||||
|
|
||||||
if char not in self.special_chars + extra_special_chars:
|
if char not in special_chars:
|
||||||
# A non-special character. Skip to the next special
|
# A non-special character. Skip to the next special
|
||||||
# character, treating the interstice as literal text.
|
# character, treating the interstice as literal text.
|
||||||
next_pos = (
|
next_pos = (
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ def human_seconds_short(interval):
|
||||||
string.
|
string.
|
||||||
"""
|
"""
|
||||||
interval = int(interval)
|
interval = int(interval)
|
||||||
return "%i:%02i" % (interval // 60, interval % 60)
|
return f"{interval // 60}:{interval % 60:02d}"
|
||||||
|
|
||||||
|
|
||||||
def human_bytes(size):
|
def human_bytes(size):
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ except ImportError as e:
|
||||||
PROTOCOL_VERSION = "0.16.0"
|
PROTOCOL_VERSION = "0.16.0"
|
||||||
BUFSIZE = 1024
|
BUFSIZE = 1024
|
||||||
|
|
||||||
HELLO = "OK MPD %s" % PROTOCOL_VERSION
|
HELLO = f"OK MPD {PROTOCOL_VERSION}"
|
||||||
CLIST_BEGIN = "command_list_begin"
|
CLIST_BEGIN = "command_list_begin"
|
||||||
CLIST_VERBOSE_BEGIN = "command_list_ok_begin"
|
CLIST_VERBOSE_BEGIN = "command_list_ok_begin"
|
||||||
CLIST_END = "command_list_end"
|
CLIST_END = "command_list_end"
|
||||||
|
|
@ -1219,7 +1219,7 @@ class Server(BaseServer):
|
||||||
if dirpath.startswith("/"):
|
if dirpath.startswith("/"):
|
||||||
# Strip leading slash (libmpc rejects this).
|
# Strip leading slash (libmpc rejects this).
|
||||||
dirpath = dirpath[1:]
|
dirpath = dirpath[1:]
|
||||||
yield "directory: %s" % dirpath
|
yield f"directory: {dirpath}"
|
||||||
|
|
||||||
def _listall(self, basepath, node, info=False):
|
def _listall(self, basepath, node, info=False):
|
||||||
"""Helper function for recursive listing. If info, show
|
"""Helper function for recursive listing. If info, show
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ def span_from_str(span_str):
|
||||||
def normalize_year(d, yearfrom):
|
def normalize_year(d, yearfrom):
|
||||||
"""Convert string to a 4 digits year"""
|
"""Convert string to a 4 digits year"""
|
||||||
if yearfrom < 100:
|
if yearfrom < 100:
|
||||||
raise BucketError("%d must be expressed on 4 digits" % yearfrom)
|
raise BucketError(f"{yearfrom} must be expressed on 4 digits")
|
||||||
|
|
||||||
# if two digits only, pick closest year that ends by these two
|
# if two digits only, pick closest year that ends by these two
|
||||||
# digits starting from yearfrom
|
# digits starting from yearfrom
|
||||||
|
|
@ -55,14 +55,13 @@ def span_from_str(span_str):
|
||||||
years = [int(x) for x in re.findall(r"\d+", span_str)]
|
years = [int(x) for x in re.findall(r"\d+", span_str)]
|
||||||
if not years:
|
if not years:
|
||||||
raise ui.UserError(
|
raise ui.UserError(
|
||||||
"invalid range defined for year bucket '%s': no year found"
|
f"invalid range defined for year bucket {span_str!r}: no year found"
|
||||||
% span_str
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
years = [normalize_year(x, years[0]) for x in years]
|
years = [normalize_year(x, years[0]) for x in years]
|
||||||
except BucketError as exc:
|
except BucketError as exc:
|
||||||
raise ui.UserError(
|
raise ui.UserError(
|
||||||
"invalid range defined for year bucket '%s': %s" % (span_str, exc)
|
f"invalid range defined for year bucket {span_str!r}: {exc}"
|
||||||
)
|
)
|
||||||
|
|
||||||
res = {"from": years[0], "str": span_str}
|
res = {"from": years[0], "str": span_str}
|
||||||
|
|
@ -126,18 +125,18 @@ def str2fmt(s):
|
||||||
"tonchars": len(m.group("toyear")),
|
"tonchars": len(m.group("toyear")),
|
||||||
}
|
}
|
||||||
res["fmt"] = (
|
res["fmt"] = (
|
||||||
f"{m['bef']}%s{m['sep']}{'%s' if res['tonchars'] else ''}{m['after']}"
|
f"{m['bef']}{{}}{m['sep']}{'{}' if res['tonchars'] else ''}{m['after']}"
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def format_span(fmt, yearfrom, yearto, fromnchars, tonchars):
|
def format_span(fmt, yearfrom, yearto, fromnchars, tonchars):
|
||||||
"""Return a span string representation."""
|
"""Return a span string representation."""
|
||||||
args = str(yearfrom)[-fromnchars:]
|
args = [str(yearfrom)[-fromnchars:]]
|
||||||
if tonchars:
|
if tonchars:
|
||||||
args = (str(yearfrom)[-fromnchars:], str(yearto)[-tonchars:])
|
args.append(str(yearto)[-tonchars:])
|
||||||
|
|
||||||
return fmt % args
|
return fmt.format(*args)
|
||||||
|
|
||||||
|
|
||||||
def extract_modes(spans):
|
def extract_modes(spans):
|
||||||
|
|
@ -166,7 +165,7 @@ def build_alpha_spans(alpha_spans_str, alpha_regexs):
|
||||||
else:
|
else:
|
||||||
raise ui.UserError(
|
raise ui.UserError(
|
||||||
"invalid range defined for alpha bucket "
|
"invalid range defined for alpha bucket "
|
||||||
"'%s': no alphanumeric character found" % elem
|
f"'{elem}': no alphanumeric character found"
|
||||||
)
|
)
|
||||||
spans.append(
|
spans.append(
|
||||||
re.compile(
|
re.compile(
|
||||||
|
|
|
||||||
|
|
@ -593,7 +593,7 @@ class CoverArtArchive(RemoteArtSource):
|
||||||
class Amazon(RemoteArtSource):
|
class Amazon(RemoteArtSource):
|
||||||
NAME = "Amazon"
|
NAME = "Amazon"
|
||||||
ID = "amazon"
|
ID = "amazon"
|
||||||
URL = "https://images.amazon.com/images/P/%s.%02i.LZZZZZZZ.jpg"
|
URL = "https://images.amazon.com/images/P/{}.{:02d}.LZZZZZZZ.jpg"
|
||||||
INDICES = (1, 2)
|
INDICES = (1, 2)
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
|
|
@ -606,7 +606,7 @@ class Amazon(RemoteArtSource):
|
||||||
if album.asin:
|
if album.asin:
|
||||||
for index in self.INDICES:
|
for index in self.INDICES:
|
||||||
yield self._candidate(
|
yield self._candidate(
|
||||||
url=self.URL % (album.asin, index),
|
url=self.URL.format(album.asin, index),
|
||||||
match=MetadataMatch.EXACT,
|
match=MetadataMatch.EXACT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ class InlineError(Exception):
|
||||||
|
|
||||||
def __init__(self, code, exc):
|
def __init__(self, code, exc):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
("error in inline path field code:\n%s\n%s: %s")
|
f"error in inline path field code:\n{code}\n{type(exc).__name__}: {exc}"
|
||||||
% (code, type(exc).__name__, str(exc))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class Amarok(MetaSource):
|
||||||
|
|
||||||
query_xml = '<query version="1.0"> \
|
query_xml = '<query version="1.0"> \
|
||||||
<filters> \
|
<filters> \
|
||||||
<and><include field="filename" value=%s /></and> \
|
<and><include field="filename" value={} /></and> \
|
||||||
</filters> \
|
</filters> \
|
||||||
</query>'
|
</query>'
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class Amarok(MetaSource):
|
||||||
# of the result set. So query for the filename and then try to match
|
# of the result set. So query for the filename and then try to match
|
||||||
# the correct item from the results we get back
|
# the correct item from the results we get back
|
||||||
results = self.collection.Query(
|
results = self.collection.Query(
|
||||||
self.query_xml % quoteattr(basename(path))
|
self.query_xml.format(quoteattr(basename(path)))
|
||||||
)
|
)
|
||||||
for result in results:
|
for result in results:
|
||||||
if result["xesam:url"] != path:
|
if result["xesam:url"] != path:
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ class MPDUpdatePlugin(BeetsPlugin):
|
||||||
return
|
return
|
||||||
|
|
||||||
if password:
|
if password:
|
||||||
s.send(b'password "%s"\n' % password.encode("utf8"))
|
s.send(f'password "{password}"\n'.encode())
|
||||||
resp = s.readline()
|
resp = s.readline()
|
||||||
if b"OK" not in resp:
|
if b"OK" not in resp:
|
||||||
self._log.warning("Authentication failed: {0!r}", resp)
|
self._log.warning("Authentication failed: {0!r}", resp)
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ class RewritePlugin(BeetsPlugin):
|
||||||
raise ui.UserError("invalid rewrite specification")
|
raise ui.UserError("invalid rewrite specification")
|
||||||
if fieldname not in library.Item._fields:
|
if fieldname not in library.Item._fields:
|
||||||
raise ui.UserError(
|
raise ui.UserError(
|
||||||
"invalid field name (%s) in rewriter" % fieldname
|
f"invalid field name ({fieldname}) in rewriter"
|
||||||
)
|
)
|
||||||
self._log.debug("adding template field {0}", key)
|
self._log.debug("adding template field {0}", key)
|
||||||
pattern = re.compile(pattern.lower())
|
pattern = re.compile(pattern.lower())
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ def json_generator(items, root, expand=False):
|
||||||
representation
|
representation
|
||||||
:returns: generator that yields strings
|
:returns: generator that yields strings
|
||||||
"""
|
"""
|
||||||
yield '{"%s":[' % root
|
yield f'{{"{root}":['
|
||||||
first = True
|
first = True
|
||||||
for item in items:
|
for item in items:
|
||||||
if first:
|
if first:
|
||||||
|
|
|
||||||
|
|
@ -384,9 +384,9 @@ Here's an example that adds a ``$disc_and_track`` field:
|
||||||
number.
|
number.
|
||||||
"""
|
"""
|
||||||
if item.disctotal > 1:
|
if item.disctotal > 1:
|
||||||
return u'%02i.%02i' % (item.disc, item.track)
|
return f"{item.disc:02d}.{item.track:02d}"
|
||||||
else:
|
else:
|
||||||
return u'%02i' % (item.track)
|
return f"{item.track:02d}"
|
||||||
|
|
||||||
With this plugin enabled, templates can reference ``$disc_and_track`` as they
|
With this plugin enabled, templates can reference ``$disc_and_track`` as they
|
||||||
can any standard metadata field.
|
can any standard metadata field.
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,7 @@ Here are a couple of examples of expressions:
|
||||||
|
|
||||||
item_fields:
|
item_fields:
|
||||||
initial: albumartist[0].upper() + u'.'
|
initial: albumartist[0].upper() + u'.'
|
||||||
disc_and_track: u'%02i.%02i' % (disc, track) if
|
disc_and_track: f"{disc:02d}.{track:02d}" if disctotal > 1 else f"{track:02d}"
|
||||||
disctotal > 1 else u'%02i' % (track)
|
|
||||||
|
|
||||||
Note that YAML syntax allows newlines in values if the subsequent lines are
|
Note that YAML syntax allows newlines in values if the subsequent lines are
|
||||||
indented.
|
indented.
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ select = [
|
||||||
"PT", # flake8-pytest-style
|
"PT", # flake8-pytest-style
|
||||||
# "RUF", # ruff
|
# "RUF", # ruff
|
||||||
# "UP", # pyupgrade
|
# "UP", # pyupgrade
|
||||||
|
"UP031", # do not use percent formatting
|
||||||
"UP032", # use f-string instead of format call
|
"UP032", # use f-string instead of format call
|
||||||
"TCH", # flake8-type-checking
|
"TCH", # flake8-type-checking
|
||||||
"W", # pycodestyle
|
"W", # pycodestyle
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class DGAlbumInfoTest(BeetsTestCase):
|
||||||
"""Return a Bag that mimics a discogs_client.Release with a
|
"""Return a Bag that mimics a discogs_client.Release with a
|
||||||
tracklist where tracks have the specified `positions`."""
|
tracklist where tracks have the specified `positions`."""
|
||||||
tracks = [
|
tracks = [
|
||||||
self._make_track("TITLE%s" % i, position)
|
self._make_track(f"TITLE{i}", position)
|
||||||
for (i, position) in enumerate(positions, start=1)
|
for (i, position) in enumerate(positions, start=1)
|
||||||
]
|
]
|
||||||
return self._make_release(tracks)
|
return self._make_release(tracks)
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
||||||
for recording in tracks:
|
for recording in tracks:
|
||||||
i += 1
|
i += 1
|
||||||
track = {
|
track = {
|
||||||
"id": "RELEASE TRACK ID %d" % i,
|
"id": f"RELEASE TRACK ID {i}",
|
||||||
"recording": recording,
|
"recording": recording,
|
||||||
"position": i,
|
"position": i,
|
||||||
"number": "A1",
|
"number": "A1",
|
||||||
|
|
@ -140,7 +140,7 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
||||||
for recording in data_tracks:
|
for recording in data_tracks:
|
||||||
i += 1
|
i += 1
|
||||||
data_track = {
|
data_track = {
|
||||||
"id": "RELEASE TRACK ID %d" % i,
|
"id": f"RELEASE TRACK ID {i}",
|
||||||
"recording": recording,
|
"recording": recording,
|
||||||
"position": i,
|
"position": i,
|
||||||
"number": "A1",
|
"number": "A1",
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ class TransactionTest(unittest.TestCase):
|
||||||
def test_query_no_increase_revision(self):
|
def test_query_no_increase_revision(self):
|
||||||
old_rev = self.db.revision
|
old_rev = self.db.revision
|
||||||
with self.db.transaction() as tx:
|
with self.db.transaction() as tx:
|
||||||
tx.query("PRAGMA table_info(%s)" % ModelFixture1._table)
|
tx.query(f"PRAGMA table_info({ModelFixture1._table})")
|
||||||
assert self.db.revision == old_rev
|
assert self.db.revision == old_rev
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1033,7 +1033,7 @@ class ArtDestinationTest(BeetsTestCase):
|
||||||
|
|
||||||
def test_art_filename_respects_setting(self):
|
def test_art_filename_respects_setting(self):
|
||||||
art = self.ai.art_destination("something.jpg")
|
art = self.ai.art_destination("something.jpg")
|
||||||
new_art = bytestring_path("%sartimage.jpg" % os.path.sep)
|
new_art = bytestring_path(f"{os.path.sep}artimage.jpg")
|
||||||
assert new_art in art
|
assert new_art in art
|
||||||
|
|
||||||
def test_art_path_in_item_dir(self):
|
def test_art_path_in_item_dir(self):
|
||||||
|
|
|
||||||
|
|
@ -1020,7 +1020,7 @@ class ConfigTest(TestPluginTestCase):
|
||||||
|
|
||||||
def test_cli_config_file_loads_plugin_commands(self):
|
def test_cli_config_file_loads_plugin_commands(self):
|
||||||
with open(self.cli_config_path, "w") as file:
|
with open(self.cli_config_path, "w") as file:
|
||||||
file.write("pluginpath: %s\n" % _common.PLUGINPATH)
|
file.write(f"pluginpath: {_common.PLUGINPATH}\n")
|
||||||
file.write("plugins: test")
|
file.write("plugins: test")
|
||||||
|
|
||||||
self.run_command("--config", self.cli_config_path, "plugin", lib=None)
|
self.run_command("--config", self.cli_config_path, "plugin", lib=None)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue