[python] throw in some flake8 plugins and adjust all Python scripts accordingly

This commit is contained in:
Dmytro Meleshko 2021-06-01 22:33:54 +03:00
parent 3dd5e39298
commit d5b147564d
23 changed files with 197 additions and 141 deletions

View file

@ -3,8 +3,7 @@
import json
import os
from abc import abstractmethod
from typing import Dict, Iterable, List, Protocol, TextIO, runtime_checkable
from typing import Dict, Iterator, List, Protocol, TextIO, runtime_checkable
__dir__ = os.path.dirname(__file__)
@ -12,16 +11,20 @@ __dir__ = os.path.dirname(__file__)
class Color:
def __init__(self, r: int, g: int, b: int) -> None:
assert 0 <= r <= 0xff
assert 0 <= g <= 0xff
assert 0 <= b <= 0xff
if not (0 <= r <= 0xff):
raise Exception("r component out of range")
if not (0 <= g <= 0xff):
raise Exception("g component out of range")
if not (0 <= b <= 0xff):
raise Exception("b component out of range")
self.r = r
self.g = g
self.b = b
@classmethod
def from_hex(cls, s: str) -> "Color":
assert len(s) == 6
if len(s) != 6:
raise Exception("hex color string must be 6 characters long")
return Color(int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16))
@property
@ -42,7 +45,7 @@ class Color:
else:
raise IndexError("color component index out of range")
def __iter__(self) -> Iterable[int]:
def __iter__(self) -> Iterator[int]:
yield self.r
yield self.g
yield self.b
@ -296,20 +299,21 @@ class ThemeGeneratorXfceTerminal(ThemeGenerator):
class ThemeGeneratorVscode(ThemeGenerator):
ANSI_COLOR_NAMES = [
"Black",
"Red",
"Green",
"Yellow",
"Blue",
"Magenta",
"Cyan",
"White",
]
def file_name(self) -> str:
return "vscode-colorCustomizations.json"
def generate(self, theme: Theme, output: TextIO) -> None:
ANSI_COLOR_NAMES = [
"Black",
"Red",
"Green",
"Yellow",
"Blue",
"Magenta",
"Cyan",
"White",
]
colors: Dict[str, str] = {
"terminal.background": theme.bg.css_hex,
@ -320,8 +324,8 @@ class ThemeGeneratorVscode(ThemeGenerator):
}
for is_bright in [False, True]:
for color_index, color_name in enumerate(ANSI_COLOR_NAMES):
color = theme.ansi_colors[color_index + int(is_bright) * len(ANSI_COLOR_NAMES)]
for color_index, color_name in enumerate(self.ANSI_COLOR_NAMES):
color = theme.ansi_colors[color_index + int(is_bright) * len(self.ANSI_COLOR_NAMES)]
colors["terminal.ansi" + ("Bright" if is_bright else "") + color_name] = color.css_hex
json.dump(colors, output, ensure_ascii=False, indent=2)
@ -341,7 +345,7 @@ class ThemeGeneratorIterm(ThemeGenerator):
output.write('<plist version="1.0">\n')
output.write("<dict>\n")
def write_color(key_name, color):
def write_color(key_name: str, color: Color) -> None:
r, g, b = (float(component) / 0xff for component in color)
output.write(" <key>{} Color</key>\n".format(key_name))
output.write(" <dict>\n")

View file

@ -3,6 +3,10 @@
{
"root": "scripts",
"extraPaths": ["script-resources"]
},
{
"root": "script-resources/welcome",
"extraPaths": ["script-resources/welcome"]
}
]
}

View file

@ -1,4 +1,27 @@
[flake8]
select =
# <https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes>
E W
# <https://flake8.pycqa.org/en/latest/user/error-codes.html#error-violation-codes>
F
# <https://github.com/zheller/flake8-quotes#warnings>
Q0
# <https://github.com/sco1/flake8-annotations#table-of-warnings>
ANN
# <https://github.com/PyCQA/pep8-naming#error-codes>
N8
# <https://github.com/gforcada/flake8-isort#error-codes>
I0
# <https://github.com/PyCQA/flake8-commas/>
C8
# <https://bandit.readthedocs.io/en/latest/plugins/index.html#complete-test-plugin-listing>
S
# Also see this:
# <https://github.com/wemake-services/wemake-python-styleguide>
# <https://wemake-python-stylegui.de/en/latest/pages/usage/violations/index.html>
ignore =
# Indent is not a multiple of 4
E111
@ -13,8 +36,17 @@ ignore =
# Line too long
E501
# `except` without an exception type
E722
# NOTE: write `except Exception` (or `except BaseException` if absolutely
# necessary) instead
# E722
# Newline before a binary operator
W503
# Newline after a binary operator
W504
# Missing type annotations for `self` and `cls` respectively
ANN101 ANN102
inline-quotes = "
multiline-quotes = "
docstring-quotes = "

View file

@ -3,7 +3,6 @@ based_on_style = google
column_limit = 99
indent_width = 2
continuation_indent_width = 2
blank_lines_between_top_level_imports_and_variables = 2
dedent_closing_brackets = true
coalesce_brackets = true
spaces_around_power_operator = true

View file

@ -1,6 +1,6 @@
import sys
import os
import subprocess
import sys
from pathlib import Path
from typing import Iterable, NoReturn

View file

@ -5,7 +5,7 @@
# <https://www.devdungeon.com/content/working-binary-data-python>
import struct
from typing import Any, IO
from typing import IO, Any
def read_bool(buf: IO[bytes]) -> bool:

View file

@ -2,7 +2,7 @@ from math import *
from fractions import Fraction
def factors(n):
def factors(n: int) -> "set[int]":
result = set()
for i in range(1, int(sqrt(n)) + 1):
if n % i == 0:
@ -11,7 +11,7 @@ def factors(n):
return result
def solve_quadratic(a, b, c):
def solve_quadratic(a: int, b: int, c: int) -> None:
if a == 0:
raise Exception("not a quadratic equation")
else:

View file

@ -1,22 +1,25 @@
from typing import List
from colorama import Fore, Style, ansi
COLORS = [ansi.code_to_chars(30 + color_index) for color_index in range(0, 8)]
COLORS: List[str] = [ansi.code_to_chars(30 + color_index) for color_index in range(0, 8)]
def colored(string, *colors):
def colored(string: str, *colors: str) -> str:
return "".join(colors + (string, Style.RESET_ALL))
def bright_colored(string, *colors):
def bright_colored(string: str, *colors: str) -> str:
return "".join(colors + (Style.BRIGHT, string, Style.RESET_ALL))
def colorize_percent(percent, warning, critical, inverse=False):
COLORS = [Fore.GREEN, Fore.YELLOW, Fore.RED]
def colorize_percent(
percent: float, warning: float, critical: float, inverse: bool = False
) -> str:
colors = [Fore.GREEN, Fore.YELLOW, Fore.RED]
color_index = 0 if percent < warning else 1 if percent < critical else 2
if inverse:
color_index = 2 - color_index
return colored("%.2f%%" % percent, COLORS[color_index])
return colored("%.2f%%" % percent, colors[color_index])

View file

@ -1,11 +1,15 @@
def humanize_timedelta(timedelta):
result = []
from datetime import timedelta
from typing import List, Tuple
def humanize_timedelta(timedelta: timedelta) -> str:
result: List[str] = []
days = timedelta.days
mm, ss = divmod(timedelta.seconds, 60)
hh, mm = divmod(mm, 60)
def plural(n):
def plural(n: int) -> Tuple[int, str]:
return n, "s" if abs(n) != 1 else ""
if days > 0:
@ -20,7 +24,7 @@ def humanize_timedelta(timedelta):
return ", ".join(result)
def humanize_bytes(bytes):
def humanize_bytes(bytes: int) -> str:
units = ["B", "kB", "MB", "GB"]
factor = 1

View file

@ -5,7 +5,6 @@ import re
from colors import COLORS, Style
from system_info import get_system_info
print("")
logo_lines, info_lines = get_system_info()

View file

@ -3,17 +3,17 @@ import platform
import socket
from datetime import datetime, timedelta
from getpass import getuser
from typing import Dict, List, Optional, Tuple, cast
import psutil
from colors import Fore, Style, bright_colored, colored, colorize_percent
from humanize import humanize_bytes, humanize_timedelta
def get_system_info():
info_lines = []
def get_system_info() -> Tuple[List[str], List[str]]:
info_lines: List[str] = []
def info(name, value, *format_args):
def info(name: str, value: str, *format_args) -> None:
line = bright_colored(name + ":", Fore.YELLOW) + " " + value
if format_args:
line = line % format_args
@ -66,27 +66,27 @@ def get_system_info():
return logo_lines, info_lines
def _get_hostname():
def _get_hostname() -> str:
hostname = socket.gethostname()
return hostname
def _get_uptime():
def _get_uptime() -> timedelta:
return datetime.now() - datetime.fromtimestamp(psutil.boot_time())
def _get_users():
users = {}
def _get_users() -> str:
users: Dict[str, List[str]] = {}
for user in psutil.users():
name = user.name
terminal = user.terminal
name: str = user.name
terminal: str = user.terminal
if name in users:
users[name].append(terminal)
else:
users[name] = [terminal]
result = []
result: List[str] = []
for name in users:
terminals = users[name]
@ -102,13 +102,13 @@ def _get_users():
return ", ".join(result)
def _get_shell():
def _get_shell() -> Optional[str]:
return os.environ.get("SHELL")
def _get_cpu_usage():
def _get_cpu_usage() -> Optional[str]:
try:
percent = psutil.cpu_percent()
percent = cast(float, psutil.cpu_percent())
except Exception as e:
print("Error in _get_cpu_usage:", e)
return None
@ -116,7 +116,7 @@ def _get_cpu_usage():
return colorize_percent(percent, warning=60, critical=80)
def _get_memory():
def _get_memory() -> Tuple[str, str, str]:
memory = psutil.virtual_memory()
return (
humanize_bytes(memory.used),
@ -125,8 +125,8 @@ def _get_memory():
)
def _get_disks():
result = []
def _get_disks() -> List[Tuple[str, str, str, str]]:
result: List[Tuple[str, str, str, str]] = []
for disk in psutil.disk_partitions(all=False):
if psutil.WINDOWS and ("cdrom" in disk.opts or disk.fstype == ""):
@ -146,7 +146,7 @@ def _get_disks():
return result
def _get_battery():
def _get_battery() -> Optional[Tuple[str, str]]:
if not hasattr(psutil, "sensors_battery"):
return None
@ -167,8 +167,8 @@ def _get_battery():
return colorize_percent(percent, critical=10, warning=20, inverse=True), status
def _get_local_ipv4_addresses():
result = []
def _get_local_ipv4_addresses() -> List[Tuple[str, str]]:
result: List[Tuple[str, str]] = []
for interface, addresses in psutil.net_if_addrs().items():
for address in addresses:
@ -184,7 +184,7 @@ def _get_local_ipv4_addresses():
return result
def _get_distro_info():
def _get_distro_info() -> Tuple[str, str, str, str]:
if psutil.WINDOWS:
return "windows", platform.system(), platform.release(), ""
elif psutil.OSX:
@ -194,9 +194,13 @@ def _get_distro_info():
sw_vers = plistlib.load(f)
return "mac", sw_vers["ProductName"], sw_vers["ProductVersion"], ""
elif _is_android():
from subprocess import check_output
import subprocess
android_version = check_output(["getprop", "ro.build.version.release"])
android_version = subprocess.run(
["getprop", "ro.build.version.release"],
check=True,
stdout=subprocess.PIPE,
).stdout
return "android", "Android", android_version.decode().strip(), ""
elif psutil.LINUX:
import distro
@ -206,5 +210,5 @@ def _get_distro_info():
raise NotImplementedError("unsupported OS")
def _is_android():
def _is_android() -> bool:
return os.path.isdir("/system/app") and os.path.isdir("/system/priv-app")

View file

@ -1,19 +1,17 @@
#!/usr/bin/env python3
import sys
import os
from pathlib import Path
from configparser import ConfigParser
import json
from typing import Any, Generator, Optional, Union
import os
import sys
import urllib.parse
import urllib.request
from configparser import ConfigParser
from pathlib import Path
from typing import Any, Generator, Optional, Union
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources"))
import common_script_utils
DEFAULT_REGISTRY_DUMP_URL = "https://stronghold.crosscode.ru/~ccbot/emote-registry.json"
if os.name == "posix":
@ -49,7 +47,8 @@ def emote_downloader_and_iterator() -> Generator[str, None, None]:
with urllib.request.urlopen(registry_dump_url, timeout=10) as response:
emote_registry_data = json.load(response)
assert emote_registry_data["version"] == 1
if emote_registry_data["version"] != 1:
raise Exception("unsupported emote registry version")
allow_nsfw = config.getboolean("default", "allow_nsfw", fallback=False)
emotes = [emote for emote in emote_registry_data["list"] if emote["safe"] or allow_nsfw]

View file

@ -1,19 +1,19 @@
#!/usr/bin/env python3
import base64
import argparse
import base64
import sys
from hashlib import md5
from typing import IO
from Crypto.Cipher import AES
from Crypto import Random
import sys
from Crypto import Random
from Crypto.Cipher import AES
CC_ENCRYPTION_MARKER_BYTES = b"[-!_0_!-]"
CC_ENCRYPTION_PASSPHRASE = b":_.NaN0"
def main():
def main() -> None:
parser = argparse.ArgumentParser()
# NOTE: Empty help strings are necessary for subparsers to show up in help.
subparsers = parser.add_subparsers(required=True, metavar="COMMAND")
@ -34,14 +34,15 @@ def main():
def cmd_pipe_decrypt(args: argparse.Namespace) -> None:
input_file: IO[bytes] = (
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, 'rb')
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, "rb")
)
output_file: IO[bytes] = (
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, 'wb')
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, "wb")
)
encrypted = input_file.read()
assert encrypted.startswith(CC_ENCRYPTION_MARKER_BYTES)
if not encrypted.startswith(CC_ENCRYPTION_MARKER_BYTES):
raise Exception()
encrypted = encrypted[len(CC_ENCRYPTION_MARKER_BYTES):]
decrypted = CryptoJsBridge.decrypt(encrypted, CC_ENCRYPTION_PASSPHRASE)
output_file.write(decrypted)
@ -49,10 +50,10 @@ def cmd_pipe_decrypt(args: argparse.Namespace) -> None:
def cmd_pipe_encrypt(args: argparse.Namespace) -> None:
input_file: IO[bytes] = (
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, 'rb')
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, "rb")
)
output_file: IO[bytes] = (
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, 'wb')
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, "wb")
)
decrypted = input_file.read()
@ -87,7 +88,8 @@ class CryptoJsBridge:
"""
Extended from <https://gist.github.com/gsakkis/4546068/1d65cea035562e36da2cc160d6a9e4821b553fa8#file-aes-py-L49-L57>.
"""
assert len(salt) == cls.SALT_SIZE
if len(salt) != cls.SALT_SIZE:
raise Exception("invalid salt length")
data += salt
key = md5(data).digest()
final_key = key
@ -114,7 +116,8 @@ class CryptoJsBridge:
Equivalent to `CryptoJS.AES.decrypt(encrypted, passphrase).toString(CryptoJS.enc.Utf8)`.
"""
encrypted = base64.b64decode(encrypted)
assert encrypted.startswith(cls.SALTED_MARKER)
if not encrypted.startswith(cls.SALTED_MARKER):
raise Exception("expected salt marker")
encrypted = encrypted[len(cls.SALTED_MARKER):]
salt, ciphertext = encrypted[:cls.SALT_SIZE], encrypted[cls.SALT_SIZE:]
key_iv = cls.bytes_to_key(passphrase, salt, cls.KEY_SIZE + cls.IV_SIZE)

View file

@ -3,16 +3,16 @@
# https://discord.com/developers/docs/reference#snowflakes
import sys
import colorama
import time
import colorama
DISCORD_EPOCH = 1420070400000 # milliseconds
user_snowflake = int(sys.argv[1])
def print_field(name, value):
def print_field(name: str, value: object) -> None:
print(
"{}{}:{} {}".format(colorama.Style.BRIGHT, name.rjust(21), colorama.Style.RESET_ALL, value)
)

View file

@ -1,9 +1,10 @@
#!/usr/bin/env python3
import discord
import sys
import os
import sys
from typing import Optional, cast
import discord
guild_id = int(sys.argv[1])
voice_channel_id = int(sys.argv[2])
@ -16,17 +17,17 @@ bot = discord.Client()
@bot.event
async def on_ready():
async def on_ready() -> None:
print("logged in as {0} ({0.id})".format(bot.user))
guild: discord.Guild = bot.get_guild(guild_id)
guild: Optional[discord.Guild] = bot.get_guild(guild_id)
if guild is None:
raise Exception("guild not found")
voice_channel: discord.VoiceChannel = guild.get_channel(voice_channel_id)
voice_channel: Optional[discord.VoiceChannel] = guild.get_channel(voice_channel_id)
if voice_channel is None:
raise Exception("channel not found")
voice_client = await voice_channel.connect()
voice_client = cast(discord.voice_client.VoiceClient, await voice_channel.connect())
print("connected to {0} ({0.id}) in {1} ({1.id})".format(voice_channel, guild))
source = discord.FFmpegPCMAudio(pulseaudio_device, before_options="-f pulse")

View file

@ -4,15 +4,16 @@
# <https://discord.com/developers/docs/resources/user#user-object#get-user>
# <https://discord.com/developers/docs/reference>
import sys
import os
import urllib.request
import urllib.error
import colorama
import time
import argparse
import json
import os
import sys
import time
import urllib.error
import urllib.request
from typing import Dict, List, Optional
import colorama
DISCORD_EPOCH = 1420070400000 # milliseconds
# https://discord.com/developers/docs/resources/user#user-object-user-flags
@ -37,17 +38,17 @@ parser.add_argument("user_snowflake", type=int)
parser.add_argument("--bot-token", type=str)
parser.add_argument("--image-size", type=int)
parser.add_argument("--get-prop", type=str)
parser.add_argument("--api-response", action='store_true')
parser.add_argument("--api-response", action="store_true")
cli_args = parser.parse_args()
user_snowflake = cli_args.user_snowflake
user_snowflake: int = cli_args.user_snowflake
bot_token = cli_args.bot_token
bot_token: Optional[str] = cli_args.bot_token
if bot_token is None:
with open(os.path.expanduser("~/.config/dotfiles/discord-tools-bot-token.txt")) as f:
bot_token = f.read().strip()
image_size = cli_args.image_size
image_size: Optional[int] = cli_args.image_size
if not (image_size is None or (image_size > 0 and image_size & (image_size - 1)) == 0):
parser.error("image_size must be greater than zero and a power of two")
@ -70,10 +71,10 @@ except urllib.error.HTTPError as err:
if cli_args.api_response:
json.dump(raw_data, sys.stdout, ensure_ascii=False, indent=2)
sys.stdout.write('\n')
sys.stdout.write("\n")
sys.exit()
data = {}
data: Dict[str, str] = {}
data["ID"] = raw_data["id"]
data["Name"] = "{}#{}".format(raw_data["username"], raw_data["discriminator"])
@ -108,7 +109,7 @@ user_flags = raw_data["public_flags"]
if user_flags == 0:
data["Flags"] = "<none>"
else:
user_flag_names = []
user_flag_names: List[str] = []
for flag_name, bitmask in DISCORD_FLAGS.items():
if user_flags & bitmask:
user_flag_names.append(flag_name)

View file

@ -4,12 +4,11 @@
# <https://www.dropbox.com/sh/uscmj9y3cjfwpsr/AAD35_ZZu64EBi0awLA07fxga?dl=0>
# <https://github.com/credomane/factoriomodsettings/blob/master/src/FactorioModSettings.js>
import sys
import os
from pathlib import Path
import struct
import json
import os
import struct
import sys
from pathlib import Path
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources"))
import factorio.property_tree
@ -23,7 +22,8 @@ with open(Path.home() / ".factorio" / "mods" / "mod-settings.dat", "rb") as f:
version_main, version_major, version_minor, version_developer = struct.unpack("<HHHH", f.read(8))
always_false_flag = factorio.property_tree.read_bool(f)
assert not always_false_flag
if always_false_flag:
raise Exception("the always-False-flag is True for some reason")
deserialized_data = {
"factorio_version": {

View file

@ -1,12 +1,12 @@
#!/usr/bin/env python3
import argparse
from pathlib import Path
from typing import Union
import plyvel
import json
from sys import stdout
import base64
import json
from pathlib import Path
from sys import stdout
from typing import Union
import plyvel
parser = argparse.ArgumentParser()
encoding_names = ["utf8", "base16", "base32", "base64", "base85"]
@ -32,7 +32,7 @@ def bytes_to_json(b: bytes, encoding: str) -> Union[str, list[int]]:
elif encoding == "base85":
return base64.b85encode(b).decode("ascii")
else:
assert False
raise Exception("unreachable")
key_encoding: str = cli_args.key_encoding or cli_args.encoding

View file

@ -1,13 +1,12 @@
#!/usr/bin/env python3
# Taken from <https://unix.stackexchange.com/a/509417/411555>
import gi
import sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib
from gi.repository import Gio, GLib, Gtk
rec_mgr = Gtk.RecentManager.get_default()
for arg in sys.argv[1:]:

View file

@ -39,12 +39,13 @@
# created by this script.
import argparse
import mwclient
import json
from urllib.parse import urlencode
import html
import json
import re
from typing import Dict, List, cast
from urllib.parse import urlencode
import mwclient
LANG = "en"
LANG_TEXT_DIRECTION = "ltr"
@ -116,7 +117,7 @@ MODULES_PRELOAD_SCRIPTS = {
# ported from <https://github.com/wikimedia/mediawiki/blob/c15ded31a6ca79fa65c00d151a7220632ad90b6d/includes/parser/Sanitizer.php#L1205-L1222>
def escape_css_class(class_str):
def escape_css_class(class_str: str) -> str:
class_str = re.sub(
r"""(^[0-9\-])|[\x00-\x20!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~]|\xA0""",
"_",
@ -127,8 +128,8 @@ def escape_css_class(class_str):
return class_str
def json_dumps_compact(data):
return json.dumps(data, indent=None, separators=(",", ":"))
def json_dumps_compact(data: object) -> str:
return json.dumps(data, indent=None, separators=(",", ":"), ensure_ascii=False)
parser = argparse.ArgumentParser()
@ -147,7 +148,7 @@ cli_args = parser.parse_args()
site = mwclient.Site(cli_args.site, scheme=cli_args.scheme)
def get_load_script_url(**args):
def get_load_script_url(**args: object) -> str:
return "{path}load{ext}?{args}".format(
path=site.path,
ext=site.ext,
@ -177,7 +178,11 @@ result = site.post(
)["parse"]
def get_modules(page_modules, added_modules_dict, blocked_modules_dict={}):
def get_modules(
page_modules: List[str],
added_modules_dict: Dict[str, List[str]],
blocked_modules_dict: Dict[str, List[str]] = {}
) -> List[str]:
modules = page_modules + added_modules_dict[cli_args.skin]
for blocked_module in blocked_modules_dict.get(cli_args.skin, []):
try:
@ -272,7 +277,7 @@ rendered_html = """\
),
skin=html.escape(cli_args.skin),
page_class=html.escape(escape_css_class(result["displaytitle"])),
title=html.escape(result["displaytitle"]),
title=html.escape(cast(str, result["displaytitle"])),
indicators_html="\n".join([
'<div id="mw-indicator-{}" class="mw-indicator">{}</div>'.format(
indicator["name"], indicator["*"]

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python3
import gi
import argparse
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, Pango
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk, Gtk, Pango
parser = argparse.ArgumentParser()
parser.add_argument("message", type=str, nargs="+")
@ -24,13 +24,13 @@ scrolled_window.add(label)
window.add(scrolled_window)
def on_key_release(target, event):
def on_key_release(_target, event) -> None:
key = event.keyval
if key in [Gdk.KEY_Escape, Gdk.KEY_q, Gdk.KEY_Q]:
window.close()
def on_configure(target, event):
def on_configure(target, event) -> None:
if target != window or event.type != Gdk.EventType.CONFIGURE:
return
font_desc = Pango.FontDescription()

View file

@ -7,15 +7,15 @@
# TODO: Update the menu on player status changes.
import math
import gi
import sys
import gi
gi.require_version("Playerctl", "2.0")
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
gi.require_version("Pango", "1.0")
from gi.repository import Playerctl, Gtk, Gdk, GLib, Pango
from gi.repository import Gdk, GLib, Gtk, Pango, Playerctl
# Larger priority values will make the player with this name appear higher in
# the menu. The default priority is 0.
@ -39,7 +39,7 @@ PLAYER_PLAYBACK_STATUS_EMOJIS = {
}
def humanize_duration(duration):
def humanize_duration(duration: float) -> str:
minutes, seconds = divmod(math.floor(duration), 60)
hours, minutes = divmod(minutes, 60)
text = "{:02}:{:02}".format(minutes, seconds)

View file

@ -7,15 +7,14 @@
# <https://stackoverflow.com/a/740183/12005228>
# <https://wiki.mozilla.org/Places:BookmarksComments>
import sys
import os
from pathlib import Path
from configparser import ConfigParser
import tempfile
import shutil
import sqlite3
from typing import Optional, Tuple, Generator
import sys
import tempfile
from configparser import ConfigParser
from pathlib import Path
from typing import Generator, Optional, Tuple
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources"))
import common_script_utils