python 3.9 compat: introduce typehelpers.py for NoneType, UnionType, TypeAlias

This commit is contained in:
InsanePrawn 2023-04-16 20:48:48 +00:00
parent c357b0a968
commit b84d2202db
9 changed files with 34 additions and 27 deletions

View file

@ -3,11 +3,10 @@ from __future__ import annotations
from munch import Munch from munch import Munch
from typing import Any, Optional, Mapping, Union from typing import Any, Optional, Mapping, Union
from dataclass import DataClass, munchclass from dataclass import DataClass
from constants import Arch from constants import Arch
@munchclass()
class SparseProfile(DataClass): class SparseProfile(DataClass):
parent: Optional[str] parent: Optional[str]
device: Optional[str] device: Optional[str]
@ -23,7 +22,6 @@ class SparseProfile(DataClass):
return f'{type(self)}{dict.__repr__(self.toDict())}' return f'{type(self)}{dict.__repr__(self.toDict())}'
@munchclass()
class Profile(SparseProfile): class Profile(SparseProfile):
parent: Optional[str] parent: Optional[str]
device: str device: str
@ -36,12 +34,10 @@ class Profile(SparseProfile):
size_extra_mb: Union[str, int] size_extra_mb: Union[str, int]
@munchclass()
class WrapperSection(DataClass): class WrapperSection(DataClass):
type: str # NOTE: rename to 'wrapper_type' if this causes problems type: str # NOTE: rename to 'wrapper_type' if this causes problems
@munchclass()
class BuildSection(DataClass): class BuildSection(DataClass):
ccache: bool ccache: bool
clean_mode: bool clean_mode: bool
@ -50,20 +46,17 @@ class BuildSection(DataClass):
threads: int threads: int
@munchclass()
class PkgbuildsSection(DataClass): class PkgbuildsSection(DataClass):
git_repo: str git_repo: str
git_branch: str git_branch: str
@munchclass()
class PacmanSection(DataClass): class PacmanSection(DataClass):
parallel_downloads: int parallel_downloads: int
check_space: bool check_space: bool
repo_branch: str repo_branch: str
@munchclass()
class PathsSection(DataClass): class PathsSection(DataClass):
cache_dir: str cache_dir: str
chroots: str chroots: str
@ -101,7 +94,6 @@ class ProfilesSection(DataClass):
return f'{type(self)}{dict.__repr__(self.toDict())}' return f'{type(self)}{dict.__repr__(self.toDict())}'
@munchclass()
class Config(DataClass): class Config(DataClass):
wrapper: WrapperSection wrapper: WrapperSection
build: BuildSection build: BuildSection
@ -138,7 +130,6 @@ class Config(DataClass):
return Config(_vals, validate=validate) return Config(_vals, validate=validate)
@munchclass()
class RuntimeConfiguration(DataClass): class RuntimeConfiguration(DataClass):
verbose: bool verbose: bool
no_wrap: bool no_wrap: bool

View file

@ -1,4 +1,4 @@
from typing_extensions import TypeAlias from typehelpers import TypeAlias
FASTBOOT = 'fastboot' FASTBOOT = 'fastboot'
FLASH_PARTS = { FLASH_PARTS = {

View file

@ -3,15 +3,11 @@ from __future__ import annotations
import logging import logging
import toml import toml
from dataclasses import dataclass
from munch import Munch from munch import Munch
from toml.encoder import TomlEncoder, TomlPreserveInlineDictEncoder from toml.encoder import TomlEncoder, TomlPreserveInlineDictEncoder
from typing import ClassVar, Generator, Optional, Union, Mapping, Any, get_type_hints, get_origin, get_args, Iterable from typing import ClassVar, Generator, Optional, Union, Mapping, Any, get_type_hints, get_origin, get_args, Iterable
from types import UnionType, NoneType
from typehelpers import UnionType, NoneType
def munchclass(*args, init=False, **kwargs):
return dataclass(*args, init=init, slots=True, **kwargs)
def resolve_type_hint(hint: type, ignore_origins: list[type] = []) -> Iterable[type]: def resolve_type_hint(hint: type, ignore_origins: list[type] = []) -> Iterable[type]:
@ -73,12 +69,12 @@ class DataClass(Munch):
type_hints = cls._type_hints if type_hints is None else type_hints type_hints = cls._type_hints if type_hints is None else type_hints
if key in type_hints: if key in type_hints:
_classes = tuple[type](resolve_type_hint(type_hints[key])) _classes = tuple[type](resolve_type_hint(type_hints[key]))
optional = NoneType in _classes optional = bool(set([NoneType, None]).intersection(_classes))
if optional and value is None: if optional and value is None:
results[key] = None results[key] = None
continue continue
if issubclass(_classes[0], dict): if issubclass(_classes[0], dict):
assert isinstance(value, dict) or optional assert isinstance(value, dict) or (optional and value is None), f'{key=} is not dict: {value!r}, {_classes=}'
target_class = _classes[0] target_class = _classes[0]
if target_class in [None, NoneType, Optional]: if target_class in [None, NoneType, Optional]:
for target in _classes[1:]: for target in _classes[1:]:

View file

@ -5,7 +5,7 @@ from typing import Optional
from config.state import config from config.state import config
from constants import Arch, ARCHES from constants import Arch, ARCHES
from config.scheme import DataClass, munchclass from dataclass import DataClass
from distro.distro import get_kupfer_local from distro.distro import get_kupfer_local
from distro.package import LocalPackage from distro.package import LocalPackage
from packages.pkgbuild import Pkgbuild, _pkgbuilds_cache, discover_pkgbuilds, get_pkgbuild_by_path, init_pkgbuilds from packages.pkgbuild import Pkgbuild, _pkgbuilds_cache, discover_pkgbuilds, get_pkgbuild_by_path, init_pkgbuilds
@ -43,7 +43,6 @@ class DeviceSummary(DataClass):
return separator.join([f"{color_str(name, bold=True, use_colors=colors)}: {value}" for name, value in fields.items()]) return separator.join([f"{color_str(name, bold=True, use_colors=colors)}: {value}" for name, value in fields.items()])
@munchclass()
class Device(DataClass): class Device(DataClass):
name: str name: str
arch: Arch arch: Arch

View file

@ -2,7 +2,7 @@ import logging
import os import os
from shutil import copyfileobj from shutil import copyfileobj
from typing import Optional from typing import Optional, Union
from urllib.request import urlopen from urllib.request import urlopen
from exec.file import get_temp_dir, makedir from exec.file import get_temp_dir, makedir
@ -17,7 +17,7 @@ class BinaryPackage(PackageInfo):
arch: str arch: str
filename: str filename: str
resolved_url: Optional[str] resolved_url: Optional[str]
_desc: Optional[dict[str, str | list[str]]] _desc: Optional[dict[str, Union[str, list[str]]]]
def __init__( def __init__(
self, self,
@ -39,7 +39,7 @@ class BinaryPackage(PackageInfo):
@classmethod @classmethod
def parse_desc(clss, desc_str: str, resolved_repo_url=None): def parse_desc(clss, desc_str: str, resolved_repo_url=None):
"""Parses a desc file, returning a PackageInfo""" """Parses a desc file, returning a PackageInfo"""
desc: dict[str, str | list[str]] = {} desc: dict[str, Union[str, list[str]]] = {}
for segment in f'\n{desc_str}'.split('\n%'): for segment in f'\n{desc_str}'.split('\n%'):
if not segment.strip(): if not segment.strip():
continue continue

View file

@ -6,7 +6,7 @@ import toml
import yaml import yaml
from copy import deepcopy from copy import deepcopy
from typing import ClassVar, Optional, Mapping from typing import ClassVar, Optional, Mapping, Union
from config.state import config from config.state import config
from constants import Arch, BASE_DISTROS, KUPFER_HTTPS, REPOS_CONFIG_FILE, REPOSITORIES from constants import Arch, BASE_DISTROS, KUPFER_HTTPS, REPOS_CONFIG_FILE, REPOSITORIES
@ -33,7 +33,7 @@ class BaseDistroRepo(AbstrRepoConfig):
class RepoConfig(AbstrRepoConfig): class RepoConfig(AbstrRepoConfig):
remote_url: Optional[str | dict[Arch, str]] remote_url: Optional[Union[str, dict[Arch, str]]]
local_only: Optional[bool] local_only: Optional[bool]

View file

@ -5,7 +5,9 @@ import subprocess
from subprocess import CompletedProcess # make it easy for users of this module from subprocess import CompletedProcess # make it easy for users of this module
from shlex import quote as shell_quote from shlex import quote as shell_quote
from typing import IO, Optional, Union, TypeAlias from typing import IO, Optional, Union
from typehelpers import TypeAlias
ElevationMethod: TypeAlias = str ElevationMethod: TypeAlias = str

View file

@ -6,7 +6,7 @@ import multiprocessing
import os import os
from joblib import Parallel, delayed from joblib import Parallel, delayed
from typing import Iterable, Optional, TypeAlias from typing import Iterable, Optional
from config.state import config, ConfigStateHolder from config.state import config, ConfigStateHolder
from constants import Arch from constants import Arch
@ -16,6 +16,7 @@ from exec.file import remove_file
from logger import setup_logging from logger import setup_logging
from utils import git, git_get_branch from utils import git, git_get_branch
from wrapper import check_programs_wrap from wrapper import check_programs_wrap
from typehelpers import TypeAlias
from .srcinfo_cache import SrcinfoMetaFile from .srcinfo_cache import SrcinfoMetaFile

18
typehelpers.py Normal file
View file

@ -0,0 +1,18 @@
from typing import Union
try:
from typing import TypeAlias # type: ignore[attr-defined]
except ImportError:
from typing_extensions import TypeAlias
TypeAlias = TypeAlias
try:
from types import UnionType
except ImportError:
UnionType: TypeAlias = Union # type: ignore[no-redef]
try:
from types import NoneType
except ImportError:
NoneType: TypeAlias = type(None) # type: ignore[no-redef]