diff --git a/script-resources/common_script_utils.py b/script-resources/common_script_utils.py index fa23b02..b96e195 100644 --- a/script-resources/common_script_utils.py +++ b/script-resources/common_script_utils.py @@ -2,32 +2,19 @@ import sys import os import subprocess from pathlib import Path -from typing import Iterable, NoReturn if os.name == "posix": - DOTFILES_CONFIG_DIR: Path = Path.home() / ".config" / "dotfiles" - DOTFILES_CACHE_DIR: Path = Path.home() / ".cache" / "dotfiles" + DOTFILES_CONFIG_DIR = Path.home() / ".config" / "dotfiles" + DOTFILES_CACHE_DIR = Path.home() / ".cache" / "dotfiles" -def platform_not_supported_error() -> NoReturn: +def platform_not_supported_error(): raise Exception("platform '{}' is not supported!".format(sys.platform)) -def run_chooser( - choices: Iterable[str], prompt: str = None, async_read: bool = False -) -> int: - supports_result_index = True - if os.isatty(sys.stderr.fileno()): - process_args = [ - "fzf", - "--with-nth=2..", - "--height=50%", - "--reverse", - "--tiebreak=index", - ] - supports_result_index = False - elif sys.platform == "darwin": +def run_chooser(choices, prompt=None, async_read=False): + if sys.platform == "darwin": process_args = ["choose", "-i"] elif os.name == "posix": process_args = ["rofi", "-dmenu", "-i", "-format", "i"] @@ -43,23 +30,24 @@ def run_chooser( ) with chooser_process.stdin as pipe: - for index, choice in enumerate(choices): + for choice in choices: assert "\n" not in choice - if not supports_result_index: - pipe.write(str(index).encode()) - pipe.write(b" ") pipe.write(choice.encode()) pipe.write(b"\n") - exit_code: int = chooser_process.wait() + exit_code = chooser_process.wait() if exit_code != 0: raise Exception("chooser process failed with exit code {}".format(exit_code)) - chosen_index = int(chooser_process.stdout.read().strip().split()[0]) + chosen_index = int( + # an extra newline is inserted by rofi for whatever reason + chooser_process.stdout.read().rstrip(b"\n") + ) + return chosen_index -def send_notification(title: str, message: str, url: str = None) -> None: +def send_notification(title, message, url=None): if sys.platform == "darwin": process_args = [ "terminal-notifier", @@ -85,7 +73,7 @@ def send_notification(title: str, message: str, url: str = None) -> None: subprocess.run(process_args, check=True) -def set_clipboard(text: str) -> None: +def set_clipboard(text): # TODO: somehow merge program selection with the logic in `zsh/functions.zsh` if sys.platform == "darwin": process_args = ["pbcopy"] diff --git a/script-resources/factorio/property_tree.py b/script-resources/factorio/property_tree.py index 9275ff4..24629e2 100644 --- a/script-resources/factorio/property_tree.py +++ b/script-resources/factorio/property_tree.py @@ -5,22 +5,21 @@ # import struct -from typing import Any, IO -def read_bool(buf: IO[bytes]) -> bool: +def read_bool(buf): return buf.read(1)[0] == 1 -def read_number(buf: IO[bytes]) -> float: +def read_number(buf): return struct.unpack(" int: +def _read_length(buf): return struct.unpack(" str: +def read_string(buf): is_empty = read_bool(buf) if is_empty: return "" @@ -30,25 +29,25 @@ def read_string(buf: IO[bytes]) -> str: return buf.read(len_).decode("utf8") -def read_dictionary(buf: IO[bytes]) -> dict[str, Any]: +def read_dictionary(buf): len_ = _read_length(buf) - value: dict[str, Any] = {} + value = {} for _ in range(len_): key = read_string(buf) value[key] = read(buf) return value -def read_list(buf: IO[bytes]) -> list[Any]: +def read_list(buf): len_ = _read_length(buf) - value: list[Any] = [] + value = [] for _ in range(len_): read_string(buf) value.append(read(buf)) return value -def read(buf: IO[bytes]) -> Any: +def read(buf): type_, _any_type_flag = buf.read(2) if type_ == 0: return None diff --git a/script-resources/welcome/humanize.py b/script-resources/welcome/humanize.py index ac7c7fb..7c5ae8d 100644 --- a/script-resources/welcome/humanize.py +++ b/script-resources/welcome/humanize.py @@ -24,11 +24,10 @@ def humanize_bytes(bytes): units = ["B", "kB", "MB", "GB"] factor = 1 - unit = "" - for unit in units: + for _unit in units: next_factor = factor << 10 if bytes < next_factor: break factor = next_factor - return "%.2f %s" % (float(bytes) / factor, unit) + return "%.2f %s" % (float(bytes) / factor, _unit) diff --git a/scripts/copy-crosscode-emoji-url b/scripts/copy-crosscode-emoji-url index 1fcbdc4..3911e44 100755 --- a/scripts/copy-crosscode-emoji-url +++ b/scripts/copy-crosscode-emoji-url @@ -2,10 +2,8 @@ import sys import os -from pathlib import Path from configparser import ConfigParser import json -from typing import Any, Generator, Optional, Union import urllib.parse import urllib.request @@ -17,25 +15,23 @@ DEFAULT_REGISTRY_DUMP_URL = "https://stronghold.crosscode.ru/~ccbot/emote-regist if os.name == "posix": - config_path: Path = ( + config_path = ( common_script_utils.DOTFILES_CONFIG_DIR / "copy-crosscode-emoji-url.ini" ) - default_registry_dump_file: Path = ( - common_script_utils.DOTFILES_CACHE_DIR / "dotfiles" - ) + default_registry_dump_file = common_script_utils.DOTFILES_CACHE_DIR / "dotfiles" else: common_script_utils.platform_not_supported_error() config = ConfigParser(interpolation=None) config.read(config_path) -emotes: list[dict[str, Any]] = [] +emotes = [] -def emote_downloader_and_iterator() -> Generator[str, None, None]: +def emote_downloader_and_iterator(): global emotes - registry_dump_file: Optional[Union[str, Path]] = config.get( + registry_dump_file = config.get( "default", "ccbot_emote_registry_dump_file", fallback=None ) if registry_dump_file is not None: @@ -47,7 +43,6 @@ def emote_downloader_and_iterator() -> Generator[str, None, None]: "default", "ccbot_emote_registry_dump_url", fallback=DEFAULT_REGISTRY_DUMP_URL ) - emote_registry_data: dict[str, Any] try: with open(registry_dump_file, "r") as f: emote_registry_data = json.load(f) @@ -71,8 +66,8 @@ chosen_index = common_script_utils.run_chooser( if chosen_index >= 0: chosen_emote = emotes[chosen_index] - emote_url: urllib.parse.ParseResult = urllib.parse.urlparse(chosen_emote["url"]) - emote_url_query: dict[str, list[str]] = urllib.parse.parse_qs(emote_url.query) + emote_url = urllib.parse.urlparse(chosen_emote["url"]) + emote_url_query = urllib.parse.parse_qs(emote_url.query) if config.getboolean("default", "add_emote_name_to_url", fallback=False): emote_url_query["name"] = [chosen_emote["name"]] @@ -83,12 +78,10 @@ if chosen_index >= 0: if default_emote_image_size is not None: emote_url_query["size"] = [str(default_emote_image_size)] - emote_url_query_str = urllib.parse.urlencode(emote_url_query, doseq=True) - emote_url_str = urllib.parse.urlunparse( - emote_url._replace(query=emote_url_query_str) - ) + emote_url_query = urllib.parse.urlencode(emote_url_query, doseq=True) + emote_url = urllib.parse.urlunparse(emote_url._replace(query=emote_url_query)) - common_script_utils.set_clipboard(emote_url_str) + common_script_utils.set_clipboard(emote_url) common_script_utils.send_notification( os.path.basename(__file__), diff --git a/scripts/factorio-dump-mod-settings b/scripts/factorio-dump-mod-settings index 5f04b2e..2f817b3 100755 --- a/scripts/factorio-dump-mod-settings +++ b/scripts/factorio-dump-mod-settings @@ -16,10 +16,6 @@ import factorio.property_tree with open(Path.home() / ".factorio" / "mods" / "mod-settings.dat", "rb") as f: - version_main: int - version_major: int - version_minor: int - version_developer: int version_main, version_major, version_minor, version_developer = struct.unpack( " -# -# +# http://kb.mozillazine.org/Profiles.ini_file +# https://stackoverflow.com/a/740183/12005228 +# https://wiki.mozilla.org/Places:BookmarksComments import sys import os @@ -14,16 +14,16 @@ from configparser import ConfigParser import tempfile import shutil import sqlite3 -from typing import Optional, Tuple, Generator +import collections sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources")) import common_script_utils if sys.platform == "darwin": - firefox_home: Path = Path.home() / "Library" / "Application Support" / "Firefox" + firefox_home = Path.home() / "Library" / "Application Support" / "Firefox" elif os.name == "posix": - firefox_home: Path = Path.home() / ".mozilla" / "firefox" + firefox_home = Path.home() / ".mozilla" / "firefox" else: common_script_utils.platform_not_supported_error() @@ -31,17 +31,15 @@ else: profiles_config = ConfigParser(interpolation=None) profiles_config.read(firefox_home / "profiles.ini") -installs_sections: list[str] = [ - s for s in profiles_config.sections() if s.startswith("Install") -] +installs_sections = [s for s in profiles_config.sections() if s.startswith("Install")] if not installs_sections: raise Exception("no Firefox installations detected!") if len(installs_sections) > 1: raise Exception("multiple Firefox installations are not supported!") -profile_dir: Path = firefox_home / profiles_config.get(installs_sections[0], "Default") +profile_dir = firefox_home / profiles_config.get(installs_sections[0], "Default") # should places.sqlite be used instead? -db_path: Path = profile_dir / "weave" / "bookmarks.sqlite" +db_path = profile_dir / "weave" / "bookmarks.sqlite" if not db_path.is_file(): raise Exception("'{}' is not a file".format(db_path)) @@ -52,22 +50,17 @@ if not db_path.is_file(): db_copy_fd, db_copy_path = tempfile.mkstemp(prefix="bookmarks.", suffix=".sqlite") os.close(db_copy_fd) -chooser_entries: list[Tuple[str, str, Optional[str]]] = [] +chooser_entries = [] try: shutil.copyfile(db_path, db_copy_path) db = sqlite3.connect(db_copy_path) - urls: dict[int, str] = {} - url_id: int - url: str + urls = {} for url_id, url in db.execute("SELECT id, url FROM urls"): urls[url_id] = url - folders: dict[str, Tuple[Optional[str], str]] = {} - folder_id: str - parent_folder_id: str - folder_title: str + folders = {} for folder_id, parent_folder_id, folder_title in db.execute( "SELECT guid, parentGuid, title FROM items WHERE kind = 3 AND validity AND NOT isDeleted" ): @@ -76,29 +69,24 @@ try: folder_title, ) - url_title: str - url_id: int - url_keyword: str - parent_folder_id: str for url_title, url_id, url_keyword, parent_folder_id in db.execute( "SELECT title, urlId, keyword, parentGuid FROM items WHERE kind = 1 AND validity AND NOT isDeleted" ): url = urls[url_id] - folder_path = list[str]() - parent_folder_id_2: Optional[str] = parent_folder_id - while parent_folder_id_2 is not None: - folder = folders.get(parent_folder_id_2, None) + folder_path = collections.deque() + while parent_folder_id is not None: + folder = folders.get(parent_folder_id, None) if folder is None: # broken folder structure? folder_path.clear() break - parent_folder_id_2, folder_title = folder + parent_folder_id, folder_title = folder if folder_title is not None: - folder_path.append(folder_title) + folder_path.appendleft(folder_title) folder_path_str = ( - ("/" + "/".join(reversed(folder_path))) if len(folder_path) > 0 else None + ("/" + "/".join(folder_path)) if len(folder_path) > 0 else None ) chooser_entries.append((url_title, url, folder_path_str)) @@ -109,7 +97,7 @@ finally: os.remove(db_copy_path) -def chooser_entries_iter() -> Generator[str, None, None]: +def chooser_entries_iter(): for title, url, folder_path_str in chooser_entries: entry_items = [title, url] if folder_path_str is not None: diff --git a/scripts/random-local-ipv4 b/scripts/random-local-ipv4 index dbf02a4..058ef11 100755 --- a/scripts/random-local-ipv4 +++ b/scripts/random-local-ipv4 @@ -3,7 +3,7 @@ import random -def randbyte() -> int: +def randbyte(): return random.randrange(0, 256)