Add Typing annotation to cli/config.py

This commit is contained in:
Justin ! 2023-07-14 01:52:39 -04:00 committed by Hugo
parent 08616abbb5
commit ef8e8980d1

View file

@ -5,11 +5,15 @@ import os
import string import string
from configparser import RawConfigParser from configparser import RawConfigParser
from itertools import chain from itertools import chain
from typing import IO
from typing import Any
from typing import Generator
from .. import PROJECT_HOME from .. import PROJECT_HOME
from .. import exceptions from .. import exceptions
from ..utils import cached_property from ..utils import cached_property
from ..utils import expand_path from ..utils import expand_path
from ..vobject import Item
from .fetchparams import expand_fetch_params from .fetchparams import expand_fetch_params
from .utils import storage_class_from_config from .utils import storage_class_from_config
@ -29,10 +33,10 @@ def validate_section_name(name, section_type):
) )
def _validate_general_section(general_config): def _validate_general_section(general_config: dict[str, str]):
invalid = set(general_config) - GENERAL_ALL invalid = set(general_config) - GENERAL_ALL
missing = GENERAL_REQUIRED - set(general_config) missing = GENERAL_REQUIRED - set(general_config)
problems = [] problems: list[str] = []
if invalid: if invalid:
problems.append( problems.append(
@ -92,17 +96,22 @@ def _validate_collections_param(collections):
class _ConfigReader: class _ConfigReader:
def __init__(self, f): def __init__(self, f: IO[Any]):
self._file = f self._file: IO[Any] = f
self._parser = c = RawConfigParser() self._parser = c = RawConfigParser()
c.read_file(f) c.read_file(f)
self._seen_names = set() self._seen_names: set = set()
self._general = {} self._general: dict[str, str] = {}
self._pairs = {} self._pairs: dict[str, dict[str, str]] = {}
self._storages = {} self._storages: dict[str, dict[str, str]] = {}
def _parse_section(self, section_type, name, options): def _parse_section(
self,
section_type: str,
name: str,
options: dict[str, Any]
) -> None:
validate_section_name(name, section_type) validate_section_name(name, section_type)
if name in self._seen_names: if name in self._seen_names:
raise ValueError(f'Name "{name}" already used.') raise ValueError(f'Name "{name}" already used.')
@ -119,7 +128,9 @@ class _ConfigReader:
else: else:
raise ValueError("Unknown section type.") raise ValueError("Unknown section type.")
def parse(self): def parse(
self
) -> tuple[dict[str, str], dict[str, dict[str, str]], dict[str, dict[str, str]]]:
for section in self._parser.sections(): for section in self._parser.sections():
if " " in section: if " " in section:
section_type, name = section.split(" ", 1) section_type, name = section.split(" ", 1)
@ -145,7 +156,10 @@ class _ConfigReader:
return self._general, self._pairs, self._storages return self._general, self._pairs, self._storages
def _parse_options(items, section=None): def _parse_options(
items: list[tuple[str, str]],
section: str | None = None
) -> Generator[tuple[str, dict[str, str]], None, None]:
for key, value in items: for key, value in items:
try: try:
yield key, json.loads(value) yield key, json.loads(value)
@ -154,13 +168,18 @@ def _parse_options(items, section=None):
class Config: class Config:
def __init__(self, general, pairs, storages): def __init__(
self,
general: dict[str, str],
pairs: dict[str, dict[str, str]],
storages: dict[str, dict[str, str]]
) -> None:
self.general = general self.general = general
self.storages = storages self.storages = storages
for name, options in storages.items(): for name, options in storages.items():
options["instance_name"] = name options["instance_name"] = name
self.pairs = {} self.pairs: dict[str, PairConfig] = {}
for name, options in pairs.items(): for name, options in pairs.items():
try: try:
self.pairs[name] = PairConfig(self, name, options) self.pairs[name] = PairConfig(self, name, options)
@ -168,12 +187,12 @@ class Config:
raise exceptions.UserError(f"Pair {name}: {e}") raise exceptions.UserError(f"Pair {name}: {e}")
@classmethod @classmethod
def from_fileobject(cls, f): def from_fileobject(cls, f: IO[Any]):
reader = _ConfigReader(f) reader = _ConfigReader(f)
return cls(*reader.parse()) return cls(*reader.parse())
@classmethod @classmethod
def from_filename_or_environment(cls, fname=None): def from_filename_or_environment(cls, fname: str | None = None):
if fname is None: if fname is None:
fname = os.environ.get("VDIRSYNCER_CONFIG", None) fname = os.environ.get("VDIRSYNCER_CONFIG", None)
if fname is None: if fname is None:
@ -190,7 +209,7 @@ class Config:
except Exception as e: except Exception as e:
raise exceptions.UserError(f"Error during reading config {fname}: {e}") raise exceptions.UserError(f"Error during reading config {fname}: {e}")
def get_storage_args(self, storage_name): def get_storage_args(self, storage_name: str):
try: try:
args = self.storages[storage_name] args = self.storages[storage_name]
except KeyError: except KeyError:
@ -211,13 +230,13 @@ class Config:
class PairConfig: class PairConfig:
def __init__(self, full_config, name, options): def __init__(self, full_config: Config, name: str, options: dict[str, str]):
self._config = full_config self._config: Config = full_config
self.name = name self.name: str = name
self.name_a = options.pop("a") self.name_a: str = options.pop("a")
self.name_b = options.pop("b") self.name_b: str = options.pop("b")
self._partial_sync = options.pop("partial_sync", None) self._partial_sync: str | None = options.pop("partial_sync", None)
self.metadata = options.pop("metadata", None) or () self.metadata = options.pop("metadata", None) or ()
self.conflict_resolution = self._process_conflict_resolution_param( self.conflict_resolution = self._process_conflict_resolution_param(
@ -238,7 +257,10 @@ class PairConfig:
if options: if options:
raise ValueError("Unknown options: {}".format(", ".join(options))) raise ValueError("Unknown options: {}".format(", ".join(options)))
def _process_conflict_resolution_param(self, conflict_resolution): def _process_conflict_resolution_param(
self,
conflict_resolution: str | list[str] | None
):
if conflict_resolution in (None, "a wins", "b wins"): if conflict_resolution in (None, "a wins", "b wins"):
return conflict_resolution return conflict_resolution
elif ( elif (
@ -302,10 +324,10 @@ class PairConfig:
class CollectionConfig: class CollectionConfig:
def __init__(self, pair, name, config_a, config_b): def __init__(self, pair, name: str, config_a, config_b):
self.pair = pair self.pair = pair
self._config = pair._config self._config = pair._config
self.name = name self.name: str = name
self.config_a = config_a self.config_a = config_a
self.config_b = config_b self.config_b = config_b
@ -314,7 +336,14 @@ class CollectionConfig:
load_config = Config.from_filename_or_environment load_config = Config.from_filename_or_environment
def _resolve_conflict_via_command(a, b, command, a_name, b_name, _check_call=None): def _resolve_conflict_via_command(
a,
b,
command,
a_name,
b_name,
_check_call=None
) -> Item:
import shutil import shutil
import tempfile import tempfile