Laid a ton of groundwork and got the first AdapterManager function working

This commit is contained in:
Sumner Evans
2020-04-18 01:58:20 -06:00
parent fdb02a7e76
commit 44bd78db46
12 changed files with 584 additions and 96 deletions

View File

@@ -1,11 +1,24 @@
import abc
from typing import Any, Dict, List, Tuple
from dataclasses import dataclass
from typing import (
Any,
Dict,
Iterable,
List,
Optional,
Type,
Tuple,
Union,
)
from pathlib import Path
from .api_objects import (Playlist, PlaylistDetails)
from .api_objects import (
Playlist,
PlaylistDetails,
)
class AdapterCacheMiss(Exception):
class CacheMissError(Exception):
"""
This exception should be thrown by caching adapters when the request
data is not available or is invalid.
@@ -13,6 +26,44 @@ class AdapterCacheMiss(Exception):
pass
@dataclass
class ConfigParamDescriptor:
"""
Describes a parameter that can be used to configure an adapter. The
:class:`description`, :class:`required` and :class:`default:` should be
self-evident as to what they do.
The :class:`type` must be one of the following:
* The literal type ``str``: corresponds to a freeform text entry field in
the UI.
* The literal type ``bool``: corresponds to a checkbox in the UI.
* The literal type ``int``: corresponds to a numeric input in the UI.
* The literal string ``"password"``: corresponds to a password entry field
in the UI.
* The literal string ``"option"``: corresponds to dropdown in the UI.
The :class:`numeric_bounds` parameter only has an effect if the
:class:`type` is `int`. It specifies the min and max values that the UI
control can have.
The :class:`numeric_step` parameter only has an effect if the :class:`type`
is `int`. It specifies the step that will be taken using the "+" and "-"
buttons on the UI control (if supported).
The :class:`options` parameter only has an effect if the :class:`type` is
``"option"``. It specifies the list of options that will be available in
the dropdown in the UI.
"""
type: Union[Type, str]
description: str
required: bool = True
default: Any = None
numeric_bounds: Optional[Tuple[int, int]] = None
numeric_step: Optional[int] = None
options: Optional[Iterable[str]] = None
class Adapter(abc.ABC):
"""
Defines the interface for a Sublime Music Adapter.
@@ -27,25 +78,41 @@ class Adapter(abc.ABC):
# =========================================================================
@staticmethod
@abc.abstractmethod
def get_config_parameters() -> List[Tuple[str, str]]: # TODO fix
def get_config_parameters() -> Dict[str, ConfigParamDescriptor]:
"""
Specifies the settings which can be configured for the adapter.
Tuples of (config_key, parameter_config)
The config_key gets used in the config dict in __init__
TODO
:returns: An ordered dictionary where the keys are the name of the
configuration paramter and the values are the
:class:`ConfigParamDescriptor` object corresponding to that
configuration parameter. The order of the keys in the dictionary
correspond to the order that the configuration parameters will be
shown in the UI.
"""
@staticmethod
@abc.abstractmethod
def verify_configuration(config: Dict[str, Any]) -> Dict[str, str]:
def verify_configuration(
config: Dict[str, Any]) -> Dict[str, Optional[str]]:
"""
Specifies a function for verifying whether or not a config is valid.
Specifies a function for verifying whether or not the config is valid.
Return a dict of field: ('verification error' OR None)
:param config: The adapter configuration. The keys of are the
configuration parameter names as defined by the return value of the
:class:`get_config_parameters` function. The values are the actual
value of the configuration parameter.
TODO
It is guaranteed that all configuration parameters that are marked
as required will have a value in ``config``.
:returns: A dictionary containing varification errors. The keys of the
returned dictionary should be the same as the passed in via the
``config`` parameter. The values should be strings describing why
the corresponding value in the ``config`` dictionary is invalid.
Not all keys need be returned (for example, if there's no error for
a given configuration parameter), and returning `None` indicates no
error.
"""
@abc.abstractmethod
@@ -55,8 +122,19 @@ class Adapter(abc.ABC):
:class:`Adapter` and should be used to do whatever setup is
required for the adapter.
:param config: TODO
:param data_directory: the directory where the adapter can store data
:param config: The adapter configuration. The keys of are the
configuration parameter names as defined by the return value of the
:class:`get_config_parameters` function. The values are the actual
value of the configuration parameter.
:param data_directory: the directory where the adapter can store data.
This directory is guaranteed to exist.
"""
def shutdown(self):
"""
This function is called when the app is being closed or the server is
changing. This should be used to clean up anything that is necessary
such as writing a cache to disk, disconnecting from a server, etc.
"""
# Usage Properties
@@ -111,8 +189,8 @@ class Adapter(abc.ABC):
# =========================================================================
def get_playlists(self) -> List[Playlist]:
"""
Gets a list of all of the :class:`Playlist` objects known to the
adapter.
Gets a list of all of the :class:`sublime.adapter.api_objects.Playlist`
objects known to the adapter.
"""
raise self._check_can_error('get_playlists')
@@ -161,17 +239,32 @@ class CachingAdapter(Adapter):
:class:`CachingAdapter` and should be used to do whatever setup is
required for the adapter.
:param config: TODO
:param data_directory: the directory where the adapter can store data
:param is_cache: whether or not the adapter is being used as a cache
:param config: The adapter configuration. The keys of are the
configuration parameter names as defined by the return value of the
:class:`get_config_parameters` function. The values are the actual
value of the configuration parameter.
:param data_directory: the directory where the adapter can store data.
This directory is guaranteed to exist.
:param is_cache: whether or not the adapter is being used as a cache.
"""
# Data Ingestion Methods
# =========================================================================
@abc.abstractmethod
def ingest_new_data(self): # TODO: actually ingest data
def ingest_new_data(
self,
function_name: str,
params: Tuple[Any, ...],
data: Any,
):
"""
This function will be called after the fallback, ground-truth adapter
returns new data. This normally will happen if this adapter has a cache
miss or if the UI forces retrieval from the ground-truth adapter.
:param function_name: the name of the function that was called on the
ground truth adapter.
:param params: the parameters that were passed to the function on the
ground truth adapter.
:param data: the data that was returned by the ground truth adapter.
"""