Commit 3a996268 authored by Honnip's avatar Honnip Committed by Jan Tojnar
Browse files

gnomeExtensions/update: Modernize type annotations

Since Python 3.9, standard collections can be used with generics in type annotations:
https://docs.python.org/3.9/whatsnew/3.9.html#type-hinting-generics-in-standard-collections

Since Python 3.10, `Optional` can be written using type union with `None`:
https://docs.python.org/3.10/whatsnew/3.10.html#pep-604-new-type-union-operator
parent 3f038d83
Loading
Loading
Loading
Loading
+19 −19
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ import urllib.request
from contextlib import contextmanager
from operator import itemgetter
from pathlib import Path
from typing import List, Dict, Optional, Any, Tuple, Set
from typing import Any

# We don't want all those deprecated legacy extensions
# Group extensions by GNOME "major" version for compatibility reasons
@@ -35,15 +35,15 @@ ExtensionVersion = int
# Keep track of all names that have been used till now to detect collisions.
# This works because we deterministically process all extensions in historical order
# The outer dict level is the shell version, as we are tracking duplicates only per same Shell version.
# key: shell version, value: Dict with key: pname, value: list of UUIDs with that pname
package_name_registry: Dict[ShellVersion, Dict[PackageName, List[Uuid]]] = {}
# key: shell version, value: dict with key: pname, value: list of UUIDs with that pname
package_name_registry: dict[ShellVersion, dict[PackageName, list[Uuid]]] = {}
for shell_version in supported_versions.keys():
    package_name_registry[shell_version] = {}

updater_dir_path = Path(__file__).resolve().parent


def fetch_extension_data(uuid: str, version: str) -> Tuple[str, str]:
def fetch_extension_data(uuid: str, version: str) -> tuple[str, str]:
    """
    Download the extension and hash it. We use `nix-prefetch-url` for this for efficiency reasons.
    Returns a tuple with the hash (Nix-compatible) of the zip file's content and the base64-encoded content of its metadata.json.
@@ -90,9 +90,9 @@ def fetch_extension_data(uuid: str, version: str) -> Tuple[str, str]:


def generate_extension_versions(
    extension_version_map: Dict[ShellVersion, ExtensionVersion],
    extension_version_map: dict[ShellVersion, ExtensionVersion],
    uuid: str,
) -> Dict[ShellVersion, Dict[str, str]]:
) -> dict[ShellVersion, dict[str, str]]:
    """
    Takes in a mapping from shell versions to extension versions and transforms it the way we need it:
    - Only take one extension version per GNOME Shell major version (as per `supported_versions`)
@@ -101,10 +101,10 @@ def generate_extension_versions(
    """

    # Determine extension version per shell version
    extension_versions: Dict[ShellVersion, ExtensionVersion] = {}
    extension_versions: dict[ShellVersion, ExtensionVersion] = {}
    for shell_version, version_prefix in supported_versions.items():
        # Newest compatible extension version
        extension_version: Optional[int] = max(
        extension_version: int | None = max(
            (
                int(ext_ver)
                for shell_ver, ext_ver in extension_version_map.items()
@@ -119,7 +119,7 @@ def generate_extension_versions(
        extension_versions[shell_version] = extension_version

    # Download information once for all extension versions chosen above
    extension_info_cache: Dict[ExtensionVersion, Tuple[str, str]] = {}
    extension_info_cache: dict[ExtensionVersion, tuple[str, str]] = {}
    for extension_version in sorted(set(extension_versions.values())):
        logging.debug(f"[{uuid}] Downloading v{extension_version}")
        extension_info_cache[extension_version] = fetch_extension_data(
@@ -128,7 +128,7 @@ def generate_extension_versions(
        )

    # Fill map
    extension_versions_full: Dict[ShellVersion, Dict[str, str]] = {}
    extension_versions_full: dict[ShellVersion, dict[str, str]] = {}
    for shell_version, extension_version in extension_versions.items():
        sha256, metadata = extension_info_cache[extension_version]

@@ -143,7 +143,7 @@ def generate_extension_versions(
    return extension_versions_full


def pname_from_url(url: str) -> Tuple[str, str]:
def pname_from_url(url: str) -> tuple[str, str]:
    """
    Parse something like "/extension/1475/battery-time/" and output ("battery-time", "1475")
    """
@@ -152,7 +152,7 @@ def pname_from_url(url: str) -> Tuple[str, str]:
    return url[3], url[2]


def process_extension(extension: Dict[str, Any]) -> Optional[Dict[str, Any]]:
def process_extension(extension: dict[str, Any]) -> dict[str, Any] | None:
    """
    Process an extension. It takes in raw scraped data and downloads all the necessary information that buildGnomeExtension.nix requires

@@ -203,11 +203,11 @@ def process_extension(extension: Dict[str, Any]) -> Optional[Dict[str, Any]]:

    # Input is a mapping str -> { version: int, … }
    # We want to map shell versions to extension versions
    shell_version_map: Dict[ShellVersion, int] = {
    shell_version_map: dict[ShellVersion, int] = {
        k: v["version"] for k, v in extension["shell_version_map"].items()
    }
    # Transform shell_version_map to be more useful for us. Also throw away unwanted versions
    shell_version_map: Dict[ShellVersion, Dict[str, str]] = generate_extension_versions(shell_version_map, uuid)  # type: ignore
    shell_version_map: dict[ShellVersion, dict[str, str]] = generate_extension_versions(shell_version_map, uuid)  # type: ignore

    # No compatible versions found
    if not shell_version_map:
@@ -236,7 +236,7 @@ def process_extension(extension: Dict[str, Any]) -> Optional[Dict[str, Any]]:


@contextmanager
def request(url: str, retries: int = 5, retry_codes: List[int] = [500, 502, 503, 504]):
def request(url: str, retries: int = 5, retry_codes: list[int] = [500, 502, 503, 504]):
    for attempt in range(retries + 1):
        try:
            with urllib.request.urlopen(url) as response:
@@ -249,7 +249,7 @@ def request(url: str, retries: int = 5, retry_codes: List[int] = [500, 502, 503,
                raise e


def scrape_extensions_index() -> List[Dict[str, Any]]:
def scrape_extensions_index() -> list[dict[str, Any]]:
    """
    Scrape the list of extensions by sending search queries to the API. We simply go over it
    page by page until we hit a non-full page or a 404 error.
@@ -297,7 +297,7 @@ if __name__ == "__main__":
    raw_extensions = scrape_extensions_index()

    logging.info(f"Downloaded {len(raw_extensions)} extensions. Processing …")
    processed_extensions: List[Dict[str, Any]] = []
    processed_extensions: list[dict[str, Any]] = []
    for num, raw_extension in enumerate(raw_extensions):
        processed_extension = process_extension(raw_extension)
        if processed_extension:
@@ -354,7 +354,7 @@ if __name__ == "__main__":
            v for k, v in package_name_registry.items() if k in last_3_versions
        ]
        # Merge all package names into a single dictionary
        package_name_registry_filtered: Dict[PackageName, Set[Uuid]] = {}
        package_name_registry_filtered: dict[PackageName, set[Uuid]] = {}
        for pkgs in package_name_registry_for_versions:
            for pname, uuids in pkgs.items():
                if pname not in package_name_registry_filtered:
@@ -365,7 +365,7 @@ if __name__ == "__main__":
            k: v for k, v in package_name_registry_filtered.items() if len(v) > 1
        }
        # Convert set to list
        collisions: Dict[PackageName, List[Uuid]] = {
        collisions: dict[PackageName, list[Uuid]] = {
            k: list(v) for k, v in package_name_registry_filtered.items()
        }
        json.dump(collisions, out, indent=2, ensure_ascii=False)