Got things to a good state that can be built off of

This commit is contained in:
Sumner Evans
2020-04-17 20:33:41 -06:00
parent 0cfe31283d
commit fdb02a7e76
13 changed files with 258 additions and 55 deletions

View File

@@ -11,7 +11,7 @@ flake8-comprehensions = "*"
flake8-pep3101 = "*" flake8-pep3101 = "*"
flake8-print = "*" flake8-print = "*"
graphviz = "*" graphviz = "*"
jedi = "*" jedi = "<0.16"
lxml = "*" lxml = "*"
mypy = "*" mypy = "*"
pytest = "*" pytest = "*"
@@ -19,7 +19,6 @@ pytest-cov = "*"
rope = "*" rope = "*"
rst2html5 = "*" rst2html5 = "*"
sphinx = "*" sphinx = "*"
sphinx-autodoc-typehints = "*"
sphinx-rtd-theme = "*" sphinx-rtd-theme = "*"
termcolor = "*" termcolor = "*"
yapf = "*" yapf = "*"

16
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "1f998ec38046cff69f63ed51610a1ca33bef37635020695781f57684daa9da01" "sha256": "ec62e729c70d9ce38576ae97cfdd88b0a006886cbca1fa4ff0160383e96453a6"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@@ -447,11 +447,11 @@
}, },
"jedi": { "jedi": {
"hashes": [ "hashes": [
"sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798", "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064",
"sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030" "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.17.0" "version": "==0.15.2"
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
@@ -694,14 +694,6 @@
"index": "pypi", "index": "pypi",
"version": "==3.0.3" "version": "==3.0.3"
}, },
"sphinx-autodoc-typehints": {
"hashes": [
"sha256:27c9e6ef4f4451766ab8d08b2d8520933b97beb21c913f3df9ab2e59b56e6c6c",
"sha256:a6b3180167479aca2c4d1ed3b5cb044a70a76cccd6b38662d39288ebd9f0dff0"
],
"index": "pypi",
"version": "==1.10.3"
},
"sphinx-rtd-theme": { "sphinx-rtd-theme": {
"hashes": [ "hashes": [
"sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4", "sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4",

View File

@@ -286,6 +286,13 @@ def generate_class_for_type(type_name: str) -> str:
indent_str = ' {}' indent_str = ' {}'
if not has_properties: if not has_properties:
code.append(indent_str.format('pass')) code.append(indent_str.format('pass'))
else:
code.append('')
code.append(
indent_str.format(
'def get(self, key: str, default: Any = None) -> Any:'))
code.append(
indent_str.format(' return getattr(self, key, default)'))
return '\n'.join(code) return '\n'.join(code)
@@ -304,7 +311,7 @@ with open(output_file, 'w+') as outfile:
'from dataclasses import dataclass, field', 'from dataclasses import dataclass, field',
'from datetime import datetime', 'from datetime import datetime',
'from enum import Enum', 'from enum import Enum',
'from typing import List, Optional', 'from typing import Any, List, Optional',
'', '',
'from sublime.server.api_object import APIObject', 'from sublime.server.api_object import APIObject',
*map(generate_class_for_type, output_order), *map(generate_class_for_type, output_order),

View File

@@ -34,7 +34,7 @@ release = f'v{sublime.__version__}'
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx_autodoc_typehints', 'sphinx.ext.autosectionlabel',
'sphinx.ext.intersphinx', 'sphinx.ext.intersphinx',
'sphinx.ext.mathjax', 'sphinx.ext.mathjax',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
@@ -46,6 +46,10 @@ autodoc_default_options = {
'show-inheritance': True, 'show-inheritance': True,
'special-members': '__init__', 'special-members': '__init__',
} }
autosectionlabel_prefix_document = True
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
}
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']

View File

@@ -2,6 +2,7 @@
ignore = E402, W503, ANN002, ANN003, ANN101, ANN102, ANN204 ignore = E402, W503, ANN002, ANN003, ANN101, ANN102, ANN204
exclude = .git,__pycache__,build,dist,flatpak exclude = .git,__pycache__,build,dist,flatpak
suppress-none-returning = True suppress-none-returning = True
suppress-dummy-args = True
application-import-names = sublime application-import-names = sublime
import-order-style = edited import-order-style = edited

View File

@@ -451,7 +451,7 @@ class SublimeMusicApp(Gtk.Application):
@dbus_propagate() @dbus_propagate()
def on_refresh_window( def on_refresh_window(
self, self,
_: Any, _,
state_updates: Dict[str, Any], state_updates: Dict[str, Any],
force: bool = False, force: bool = False,
): ):
@@ -686,7 +686,7 @@ class SublimeMusicApp(Gtk.Application):
# Update the window according to the new server configuration. # Update the window according to the new server configuration.
self.update_window() self.update_window()
def on_stack_change(self, stack: Gtk.Stack, _: Any): def on_stack_change(self, stack: Gtk.Stack, _):
self.app_config.state.current_tab = stack.get_visible_child_name() self.app_config.state.current_tab = stack.get_visible_child_name()
self.update_window() self.update_window()
@@ -815,7 +815,7 @@ class SublimeMusicApp(Gtk.Application):
self.update_window() self.update_window()
@dbus_propagate() @dbus_propagate()
def on_volume_change(self, _: Any, value: float): def on_volume_change(self, _, value: float):
self.app_config.state.volume = value self.app_config.state.volume = value
self.player.volume = self.app_config.state.volume self.player.volume = self.app_config.state.volume
self.update_window() self.update_window()

View File

@@ -368,7 +368,8 @@ class CacheManager(metaclass=Singleton):
meta_json = json.load(f) meta_json = json.load(f)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
# Just continue with the default meta_json. # Just continue with the default meta_json.
pass logging.warning(
'Unable to load cache', stack_info=True)
cache_version = meta_json.get('version', 0) cache_version = meta_json.get('version', 0)
@@ -411,7 +412,6 @@ class CacheManager(metaclass=Singleton):
('artist_details', ArtistWithAlbumsID3, dict), ('artist_details', ArtistWithAlbumsID3, dict),
('artist_infos', ArtistInfo2, dict), ('artist_infos', ArtistInfo2, dict),
] ]
return
for name, type_name, default in cache_configs: for name, type_name, default in cache_configs:
if default == list: if default == list:
self.cache[name] = [ self.cache[name] = [

View File

@@ -33,25 +33,29 @@ def from_json(template_type: Any, data: Any) -> Any:
instance = None instance = None
# Handle generics. List[*], Dict[*, *] in particular. # Handle generics. List[*], Dict[*, *] in particular.
elif type(template_type) == typing._GenericAlias: # type: ignore elif type(template_type) == typing._GenericAlias: # type: ignore
# Having to use this because things changed in Python 3.7. if getattr(template_type, '__origin__') == typing.Union:
class_name = template_type._name template_type = template_type.__args__[0]
instance = from_json(template_type, data)
# This is not very elegant since it doesn't allow things which sublass
# from List or Dict. For my purposes, this doesn't matter.
if class_name == 'List':
inner_type = template_type.__args__[0]
instance = [from_json(inner_type, value) for value in data]
elif class_name == 'Dict':
key_type, val_type = template_type.__args__
instance = {
from_json(key_type, key): from_json(val_type, value)
for key, value in data.items()
}
else: else:
raise Exception( # Having to use this because things changed in Python 3.7.
'Trying to deserialize an unsupported type: {}'.format( class_name = template_type._name
template_type._name))
# This is not very elegant since it doesn't allow things which
# sublass from List or Dict. For my purposes, this doesn't matter.
if class_name == 'List':
inner_type = template_type.__args__[0]
instance = [from_json(inner_type, value) for value in data]
elif class_name == 'Dict':
key_type, val_type = template_type.__args__
instance = {
from_json(key_type, key): from_json(val_type, value)
for key, value in data.items()
}
else:
raise Exception(
'Trying to deserialize an unsupported type: {}'.format(
template_type._name))
elif template_type == str or issubclass(template_type, str): elif template_type == str or issubclass(template_type, str):
instance = data instance = data
elif template_type == int or issubclass(template_type, int): elif template_type == int or issubclass(template_type, int):
@@ -72,9 +76,13 @@ def from_json(template_type: Any, data: Any) -> Any:
# Handle everything else by first instantiating the class, then adding # Handle everything else by first instantiating the class, then adding
# all of the sub-elements, recursively calling from_json on them. # all of the sub-elements, recursively calling from_json on them.
else: else:
instance = template_type() # for field, field_type in annotations.items():
for field, field_type in annotations.items(): # value = data.get(field)
value = data.get(field) # setattr(instance, field, from_json(field_type, value))
setattr(instance, field, from_json(field_type, value)) instance = template_type(
**{
field: from_json(field_type, data.get(field))
for field, field_type in annotations.items()
})
return instance return instance

View File

@@ -1,6 +1,8 @@
from dataclasses import Field, fields # from dataclasses import Field, fields
from typing import Any, Dict from typing import Any, Dict
from sublime.from_json import from_json as _from_json
class APIObject: class APIObject:
"""Defines the base class for objects coming from the Subsonic API.""" """Defines the base class for objects coming from the Subsonic API."""
@@ -15,11 +17,4 @@ class APIObject:
:param data: a Python dictionary representation of the data to :param data: a Python dictionary representation of the data to
deserialize deserialize
""" """
if data is None: return _from_json(cls, data)
return data
print('=' * 80)
deserialized = cls.__call__(**data)
for field in fields(cls):
print(field)
value = getattr(deserialized, field.name)
print('ohea', value)

View File

@@ -8,7 +8,7 @@ script or run it on a new API version.
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from typing import List, Optional from typing import Any, List, Optional
from sublime.server.api_object import APIObject from sublime.server.api_object import APIObject
@@ -23,6 +23,9 @@ class AlbumInfo(APIObject):
largeImageUrl: List[str] = field(default_factory=list) largeImageUrl: List[str] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class AverageRating(APIObject, float): class AverageRating(APIObject, float):
@@ -35,6 +38,9 @@ class MediaType(APIObject, Enum):
AUDIOBOOK = 'audiobook' AUDIOBOOK = 'audiobook'
VIDEO = 'video' VIDEO = 'video'
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class UserRating(APIObject, int): class UserRating(APIObject, int):
@@ -76,12 +82,18 @@ class Child(APIObject):
originalWidth: Optional[int] = None originalWidth: Optional[int] = None
originalHeight: Optional[int] = None originalHeight: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class AlbumList(APIObject): class AlbumList(APIObject):
album: List[Child] = field(default_factory=list) album: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class AlbumID3(APIObject): class AlbumID3(APIObject):
@@ -99,12 +111,18 @@ class AlbumID3(APIObject):
year: Optional[int] = None year: Optional[int] = None
genre: Optional[str] = None genre: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class AlbumList2(APIObject): class AlbumList2(APIObject):
album: List[AlbumID3] = field(default_factory=list) album: List[AlbumID3] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class AlbumWithSongsID3(APIObject): class AlbumWithSongsID3(APIObject):
@@ -123,6 +141,9 @@ class AlbumWithSongsID3(APIObject):
year: Optional[int] = None year: Optional[int] = None
genre: Optional[str] = None genre: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Artist(APIObject): class Artist(APIObject):
@@ -134,6 +155,9 @@ class Artist(APIObject):
userRating: Optional[UserRating] = None userRating: Optional[UserRating] = None
averageRating: Optional[AverageRating] = None averageRating: Optional[AverageRating] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistInfoBase(APIObject): class ArtistInfoBase(APIObject):
@@ -145,6 +169,9 @@ class ArtistInfoBase(APIObject):
largeImageUrl: List[str] = field(default_factory=list) largeImageUrl: List[str] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistInfo(APIObject): class ArtistInfo(APIObject):
@@ -157,6 +184,9 @@ class ArtistInfo(APIObject):
mediumImageUrl: List[str] = field(default_factory=list) mediumImageUrl: List[str] = field(default_factory=list)
largeImageUrl: List[str] = field(default_factory=list) largeImageUrl: List[str] = field(default_factory=list)
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistID3(APIObject): class ArtistID3(APIObject):
@@ -168,6 +198,9 @@ class ArtistID3(APIObject):
artistImageUrl: Optional[str] = None artistImageUrl: Optional[str] = None
starred: Optional[datetime] = None starred: Optional[datetime] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistInfo2(APIObject): class ArtistInfo2(APIObject):
@@ -180,6 +213,9 @@ class ArtistInfo2(APIObject):
mediumImageUrl: List[str] = field(default_factory=list) mediumImageUrl: List[str] = field(default_factory=list)
largeImageUrl: List[str] = field(default_factory=list) largeImageUrl: List[str] = field(default_factory=list)
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistWithAlbumsID3(APIObject): class ArtistWithAlbumsID3(APIObject):
@@ -192,6 +228,9 @@ class ArtistWithAlbumsID3(APIObject):
artistImageUrl: Optional[str] = None artistImageUrl: Optional[str] = None
starred: Optional[datetime] = None starred: Optional[datetime] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class IndexID3(APIObject): class IndexID3(APIObject):
@@ -199,6 +238,9 @@ class IndexID3(APIObject):
artist: List[ArtistID3] = field(default_factory=list) artist: List[ArtistID3] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtistsID3(APIObject): class ArtistsID3(APIObject):
@@ -206,6 +248,9 @@ class ArtistsID3(APIObject):
index: List[IndexID3] = field(default_factory=list) index: List[IndexID3] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Bookmark(APIObject): class Bookmark(APIObject):
@@ -217,12 +262,18 @@ class Bookmark(APIObject):
value: Optional[str] = None value: Optional[str] = None
comment: Optional[str] = None comment: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Bookmarks(APIObject): class Bookmarks(APIObject):
bookmark: List[Bookmark] = field(default_factory=list) bookmark: List[Bookmark] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ChatMessage(APIObject): class ChatMessage(APIObject):
@@ -231,12 +282,18 @@ class ChatMessage(APIObject):
message: str message: str
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ChatMessages(APIObject): class ChatMessages(APIObject):
chatMessage: List[ChatMessage] = field(default_factory=list) chatMessage: List[ChatMessage] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Directory(APIObject): class Directory(APIObject):
@@ -250,6 +307,9 @@ class Directory(APIObject):
averageRating: Optional[AverageRating] = None averageRating: Optional[AverageRating] = None
playCount: Optional[int] = None playCount: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Error(APIObject): class Error(APIObject):
@@ -257,6 +317,9 @@ class Error(APIObject):
value: Optional[str] = None value: Optional[str] = None
message: Optional[str] = None message: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Genre(APIObject): class Genre(APIObject):
@@ -264,12 +327,18 @@ class Genre(APIObject):
albumCount: int albumCount: int
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Genres(APIObject): class Genres(APIObject):
genre: List[Genre] = field(default_factory=list) genre: List[Genre] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Index(APIObject): class Index(APIObject):
@@ -277,6 +346,9 @@ class Index(APIObject):
artist: List[Artist] = field(default_factory=list) artist: List[Artist] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Indexes(APIObject): class Indexes(APIObject):
@@ -287,6 +359,9 @@ class Indexes(APIObject):
child: List[Child] = field(default_factory=list) child: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class InternetRadioStation(APIObject): class InternetRadioStation(APIObject):
@@ -296,12 +371,18 @@ class InternetRadioStation(APIObject):
value: Optional[str] = None value: Optional[str] = None
homePageUrl: Optional[str] = None homePageUrl: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class InternetRadioStations(APIObject): class InternetRadioStations(APIObject):
internetRadioStation: List[InternetRadioStation] = field(default_factory=list) internetRadioStation: List[InternetRadioStation] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class JukeboxStatus(APIObject): class JukeboxStatus(APIObject):
@@ -311,6 +392,9 @@ class JukeboxStatus(APIObject):
value: Optional[str] = None value: Optional[str] = None
position: Optional[int] = None position: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class JukeboxPlaylist(APIObject): class JukeboxPlaylist(APIObject):
@@ -321,6 +405,9 @@ class JukeboxPlaylist(APIObject):
value: Optional[str] = None value: Optional[str] = None
position: Optional[int] = None position: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class License(APIObject): class License(APIObject):
@@ -330,6 +417,9 @@ class License(APIObject):
licenseExpires: Optional[datetime] = None licenseExpires: Optional[datetime] = None
trialExpires: Optional[datetime] = None trialExpires: Optional[datetime] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Lyrics(APIObject): class Lyrics(APIObject):
@@ -337,6 +427,9 @@ class Lyrics(APIObject):
value: Optional[str] = None value: Optional[str] = None
title: Optional[str] = None title: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class MusicFolder(APIObject): class MusicFolder(APIObject):
@@ -344,12 +437,18 @@ class MusicFolder(APIObject):
value: Optional[str] = None value: Optional[str] = None
name: Optional[str] = None name: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class MusicFolders(APIObject): class MusicFolders(APIObject):
musicFolder: List[MusicFolder] = field(default_factory=list) musicFolder: List[MusicFolder] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
class PodcastStatus(APIObject, Enum): class PodcastStatus(APIObject, Enum):
NEW = 'new' NEW = 'new'
@@ -359,6 +458,9 @@ class PodcastStatus(APIObject, Enum):
DELETED = 'deleted' DELETED = 'deleted'
SKIPPED = 'skipped' SKIPPED = 'skipped'
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class PodcastEpisode(APIObject): class PodcastEpisode(APIObject):
@@ -400,12 +502,18 @@ class PodcastEpisode(APIObject):
originalWidth: Optional[int] = None originalWidth: Optional[int] = None
originalHeight: Optional[int] = None originalHeight: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class NewestPodcasts(APIObject): class NewestPodcasts(APIObject):
episode: List[PodcastEpisode] = field(default_factory=list) episode: List[PodcastEpisode] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class NowPlayingEntry(APIObject): class NowPlayingEntry(APIObject):
@@ -446,12 +554,18 @@ class NowPlayingEntry(APIObject):
originalWidth: Optional[int] = None originalWidth: Optional[int] = None
originalHeight: Optional[int] = None originalHeight: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class NowPlaying(APIObject): class NowPlaying(APIObject):
entry: List[NowPlayingEntry] = field(default_factory=list) entry: List[NowPlayingEntry] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class PlayQueue(APIObject): class PlayQueue(APIObject):
@@ -463,6 +577,9 @@ class PlayQueue(APIObject):
current: Optional[int] = None current: Optional[int] = None
position: Optional[int] = None position: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Playlist(APIObject): class Playlist(APIObject):
@@ -479,6 +596,9 @@ class Playlist(APIObject):
public: Optional[bool] = None public: Optional[bool] = None
coverArt: Optional[str] = None coverArt: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class PlaylistWithSongs(APIObject): class PlaylistWithSongs(APIObject):
@@ -496,12 +616,18 @@ class PlaylistWithSongs(APIObject):
public: Optional[bool] = None public: Optional[bool] = None
coverArt: Optional[str] = None coverArt: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Playlists(APIObject): class Playlists(APIObject):
playlist: List[Playlist] = field(default_factory=list) playlist: List[Playlist] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class PodcastChannel(APIObject): class PodcastChannel(APIObject):
@@ -516,17 +642,26 @@ class PodcastChannel(APIObject):
originalImageUrl: Optional[str] = None originalImageUrl: Optional[str] = None
errorMessage: Optional[str] = None errorMessage: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Podcasts(APIObject): class Podcasts(APIObject):
channel: List[PodcastChannel] = field(default_factory=list) channel: List[PodcastChannel] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
class ResponseStatus(APIObject, Enum): class ResponseStatus(APIObject, Enum):
OK = 'ok' OK = 'ok'
FAILED = 'failed' FAILED = 'failed'
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class ScanStatus(APIObject): class ScanStatus(APIObject):
@@ -534,6 +669,9 @@ class ScanStatus(APIObject):
value: Optional[str] = None value: Optional[str] = None
count: Optional[int] = None count: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class SearchResult(APIObject): class SearchResult(APIObject):
@@ -542,6 +680,9 @@ class SearchResult(APIObject):
match: List[Child] = field(default_factory=list) match: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class SearchResult2(APIObject): class SearchResult2(APIObject):
@@ -550,6 +691,9 @@ class SearchResult2(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class SearchResult3(APIObject): class SearchResult3(APIObject):
@@ -558,6 +702,9 @@ class SearchResult3(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Share(APIObject): class Share(APIObject):
@@ -572,30 +719,45 @@ class Share(APIObject):
expires: Optional[datetime] = None expires: Optional[datetime] = None
lastVisited: Optional[datetime] = None lastVisited: Optional[datetime] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Shares(APIObject): class Shares(APIObject):
share: List[Share] = field(default_factory=list) share: List[Share] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class SimilarSongs(APIObject): class SimilarSongs(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class SimilarSongs2(APIObject): class SimilarSongs2(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Songs(APIObject): class Songs(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Starred(APIObject): class Starred(APIObject):
@@ -604,6 +766,9 @@ class Starred(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Starred2(APIObject): class Starred2(APIObject):
@@ -612,12 +777,18 @@ class Starred2(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class TopSongs(APIObject): class TopSongs(APIObject):
song: List[Child] = field(default_factory=list) song: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class User(APIObject): class User(APIObject):
@@ -641,12 +812,18 @@ class User(APIObject):
maxBitRate: Optional[int] = None maxBitRate: Optional[int] = None
avatarLastChanged: Optional[datetime] = None avatarLastChanged: Optional[datetime] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Users(APIObject): class Users(APIObject):
user: List[User] = field(default_factory=list) user: List[User] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Version(APIObject, str): class Version(APIObject, str):
@@ -660,6 +837,9 @@ class AudioTrack(APIObject):
name: Optional[str] = None name: Optional[str] = None
languageCode: Optional[str] = None languageCode: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Captions(APIObject): class Captions(APIObject):
@@ -667,6 +847,9 @@ class Captions(APIObject):
value: Optional[str] = None value: Optional[str] = None
name: Optional[str] = None name: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class VideoConversion(APIObject): class VideoConversion(APIObject):
@@ -675,6 +858,9 @@ class VideoConversion(APIObject):
bitRate: Optional[int] = None bitRate: Optional[int] = None
audioTrackId: Optional[int] = None audioTrackId: Optional[int] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class VideoInfo(APIObject): class VideoInfo(APIObject):
@@ -684,12 +870,18 @@ class VideoInfo(APIObject):
conversion: List[VideoConversion] = field(default_factory=list) conversion: List[VideoConversion] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Videos(APIObject): class Videos(APIObject):
video: List[Child] = field(default_factory=list) video: List[Child] = field(default_factory=list)
value: Optional[str] = None value: Optional[str] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)
@dataclass(frozen=True) @dataclass(frozen=True)
class Response(APIObject): class Response(APIObject):
@@ -739,3 +931,6 @@ class Response(APIObject):
value: Optional[str] = None value: Optional[str] = None
status: Optional[ResponseStatus] = None status: Optional[ResponseStatus] = None
version: Optional[Version] = None version: Optional[Version] = None
def get(self, key: str, default: Any = None) -> Any:
return getattr(self, key, default)

View File

@@ -148,8 +148,7 @@ class Server:
) )
raise Exception(f'Subsonic API Error #{code}: {message}') raise Exception(f'Subsonic API Error #{code}: {message}')
print(subsonic_response) response = Response.from_json(subsonic_response)
response = Response.from_dict(subsonic_response)
# Check for an error and if it exists, raise it. # Check for an error and if it exists, raise it.
if response.error: if response.error:

View File

@@ -66,6 +66,7 @@ class UIState:
@property @property
def current_song(self) -> Optional[Child]: def current_song(self) -> Optional[Child]:
from sublime.cache_manager import CacheManager
if (not self.play_queue or self.current_song_index < 0 if (not self.play_queue or self.current_song_index < 0
or not CacheManager.ready()): or not CacheManager.ready()):
return None return None

View File

@@ -2,10 +2,12 @@ import pytest
from sublime.adapters import Adapter, AdapterManager from sublime.adapters import Adapter, AdapterManager
def test_adapter_manager_singleton(): def test_adapter_manager_singleton():
AdapterManager.reset() AdapterManager.reset()
AdapterManager.get_playlists() AdapterManager.get_playlists()
def test_functions_not_implemented(): def test_functions_not_implemented():
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
Adapter(None) Adapter(None)