89 lines
2.6 KiB
Python
89 lines
2.6 KiB
Python
import inspect
|
|
from typing import Dict, List, Any
|
|
from datetime import datetime
|
|
from dateutil import parser
|
|
|
|
|
|
class APIObject:
|
|
@classmethod
|
|
def from_json(cls, data):
|
|
"""
|
|
Approach for deserialization here:
|
|
https://stackoverflow.com/a/40639688/2319844
|
|
"""
|
|
annotations: Dict[str, Any] = getattr(cls, '__annotations__', {})
|
|
|
|
# Handle lists of objects.
|
|
if issubclass(cls, List):
|
|
list_type = cls.__args__[0]
|
|
instance: List[list_type] = list()
|
|
for value in data:
|
|
instance.append(list_type.from_json(value))
|
|
|
|
# Handle dictionaries of objects.
|
|
elif issubclass(cls, Dict):
|
|
key_type = cls.__args__[0]
|
|
val_type = cls.__args__[1]
|
|
instance: Dict[key_type, val_type] = dict()
|
|
for key, value in data.items():
|
|
instance.update(key_type.from_json(key),
|
|
key_type.from_json(value))
|
|
|
|
# Handle everything else by first instantiating the class, then adding
|
|
# all of the sub-elements, recursively calling from_json on them.
|
|
else:
|
|
instance: cls = cls()
|
|
for name, value in data.items():
|
|
field_type = annotations.get(name)
|
|
print('ohea', field_type, value)
|
|
if inspect.isclass(field_type):
|
|
if isinstance(value, (dict, tuple, list, set, frozenset)):
|
|
setattr(instance, name, field_type.from_json(value))
|
|
elif field_type == datetime:
|
|
setattr(instance, name, parser.parse(value))
|
|
else:
|
|
setattr(instance, name, value)
|
|
else:
|
|
setattr(instance, name, value)
|
|
|
|
return instance
|
|
|
|
def get(self, field, default=None):
|
|
return getattr(self, field, default)
|
|
|
|
def __repr__(self):
|
|
annotations: Dict[str, Any] = self.__annotations__
|
|
typename = type(self).__name__
|
|
fieldstr = ' '.join([
|
|
f'{field}={getattr(self, field)!r}'
|
|
for field in annotations.keys() if hasattr(self, field)
|
|
])
|
|
return f'<{typename} {fieldstr}>'
|
|
|
|
|
|
class SubsonicError(APIObject):
|
|
code: int
|
|
message: str
|
|
|
|
def as_exception(self):
|
|
return Exception(f'{self.code}: {self.message}')
|
|
|
|
|
|
class License(APIObject):
|
|
valid: bool
|
|
email: str
|
|
licenseExpires: datetime
|
|
|
|
|
|
class MusicFolder(APIObject):
|
|
id: int
|
|
name: str
|
|
|
|
|
|
class SubsonicResponse(APIObject):
|
|
status: str
|
|
version: str
|
|
license: License
|
|
error: SubsonicError
|
|
musicFolders: List[MusicFolder]
|