jack1142 929fd04613
EUD things for Downloader (#4169)
* Initial commit

* Send pagified as with other Downloader's things
2020-08-17 01:27:46 +02:00

232 lines
6.8 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Union, cast
from redbot import VersionInfo, version_info as red_version_info
from . import installable
from .log import log
if TYPE_CHECKING:
from .json_mixins import RepoJSONMixin
__all__ = ("REPO_SCHEMA", "INSTALLABLE_SCHEMA", "update_mixin")
class UseDefault:
"""To be used as sentinel."""
# sentinel value
USE_DEFAULT = UseDefault()
def ensure_tuple_of_str(
info_file: Path, key_name: str, value: Union[Any, UseDefault]
) -> Tuple[str, ...]:
default: Tuple[str, ...] = ()
if value is USE_DEFAULT:
return default
if not isinstance(value, list):
log.warning(
"Invalid value of '%s' key (expected list, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
for item in value:
if not isinstance(item, str):
log.warning(
"Invalid item in '%s' list (expected str, got %s)"
" in JSON information file at path: %s",
key_name,
type(item).__name__,
info_file,
)
return default
return tuple(value)
def ensure_str(info_file: Path, key_name: str, value: Union[Any, UseDefault]) -> str:
default = ""
if value is USE_DEFAULT:
return default
if not isinstance(value, str):
log.warning(
"Invalid value of '%s' key (expected str, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
return value
def ensure_red_version_info(
info_file: Path, key_name: str, value: Union[Any, UseDefault]
) -> VersionInfo:
default = red_version_info
if value is USE_DEFAULT:
return default
if not isinstance(value, str):
log.warning(
"Invalid value of '%s' key (expected str, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
try:
version_info = VersionInfo.from_str(value)
except ValueError:
log.warning(
"Invalid value of '%s' key (given value isn't a valid version string)"
" in JSON information file at path: %s",
key_name,
info_file,
)
return default
return version_info
def ensure_python_version_info(
info_file: Path, key_name: str, value: Union[Any, UseDefault]
) -> Tuple[int, int, int]:
default = (3, 5, 1)
if value is USE_DEFAULT:
return default
if not isinstance(value, list):
log.warning(
"Invalid value of '%s' key (expected list, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
count = len(value)
if count != 3:
log.warning(
"Invalid value of '%s' key (expected list with 3 items, got %s items)"
" in JSON information file at path: %s",
key_name,
count,
info_file,
)
return default
for item in value:
if not isinstance(item, int):
log.warning(
"Invalid item in '%s' list (expected int, got %s)"
" in JSON information file at path: %s",
key_name,
type(item).__name__,
info_file,
)
return default
return cast(Tuple[int, int, int], tuple(value))
def ensure_bool(
info_file: Path, key_name: str, value: Union[Any, UseDefault], *, default: bool = False
) -> bool:
if value is USE_DEFAULT:
return default
if not isinstance(value, bool):
log.warning(
"Invalid value of '%s' key (expected bool, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
return value
def ensure_required_cogs_mapping(
info_file: Path, key_name: str, value: Union[Any, UseDefault]
) -> Dict[str, str]:
default: Dict[str, str] = {}
if value is USE_DEFAULT:
return default
if not isinstance(value, dict):
log.warning(
"Invalid value of '%s' key (expected dict, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default
# keys in json dicts are always strings
for item in value.values():
if not isinstance(item, str):
log.warning(
"Invalid item in '%s' dict (expected str, got %s)"
" in JSON information file at path: %s",
key_name,
type(item).__name__,
info_file,
)
return default
return value
def ensure_installable_type(
info_file: Path, key_name: str, value: Union[Any, UseDefault]
) -> installable.InstallableType:
default = installable.InstallableType.COG
if value is USE_DEFAULT:
return default
if not isinstance(value, str):
log.warning(
"Invalid value of '%s' key (expected str, got %s)"
" in JSON information file at path: %s",
key_name,
type(value).__name__,
info_file,
)
return default # NOTE: old behavior was to use InstallableType.UNKNOWN
if value in ("", "COG"):
return installable.InstallableType.COG
if value == "SHARED_LIBRARY":
return installable.InstallableType.SHARED_LIBRARY
return installable.InstallableType.UNKNOWN
EnsureCallable = Callable[[Path, str, Union[Any, UseDefault]], Any]
SchemaType = Dict[str, EnsureCallable]
REPO_SCHEMA: SchemaType = {
"author": ensure_tuple_of_str,
"description": ensure_str,
"install_msg": ensure_str,
"short": ensure_str,
}
INSTALLABLE_SCHEMA: SchemaType = {
"min_bot_version": ensure_red_version_info,
"max_bot_version": ensure_red_version_info,
"min_python_version": ensure_python_version_info,
"hidden": ensure_bool,
"disabled": ensure_bool,
"required_cogs": ensure_required_cogs_mapping,
"requirements": ensure_tuple_of_str,
"tags": ensure_tuple_of_str,
"type": ensure_installable_type,
"end_user_data_statement": ensure_str,
}
def update_mixin(repo_or_installable: RepoJSONMixin, schema: SchemaType) -> None:
info = repo_or_installable._info
info_file = repo_or_installable._info_file
for key, callback in schema.items():
setattr(repo_or_installable, key, callback(info_file, key, info.get(key, USE_DEFAULT)))