lexicon-python 0.1.2__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lexicon/__init__.py +20 -2
- lexicon/client.py +162 -0
- lexicon/resources/__init__.py +15 -0
- lexicon/resources/_common_types.py +228 -0
- lexicon/resources/base.py +69 -0
- lexicon/resources/playlist_tracks.py +302 -0
- lexicon/resources/playlist_tracks_types.py +5 -0
- lexicon/resources/playlists.py +601 -0
- lexicon/resources/playlists_types.py +129 -0
- lexicon/resources/tag_categories.py +253 -0
- lexicon/resources/tag_categories_types.py +23 -0
- lexicon/resources/tags.py +244 -0
- lexicon/resources/tags_types.py +26 -0
- lexicon/resources/tracks.py +765 -0
- lexicon/resources/tracks_types.py +898 -0
- lexicon/tools/__init__.py +1 -0
- lexicon/tools/playlists.py +163 -0
- lexicon/tools/tracks.py +130 -0
- lexicon/utils.py +17 -0
- lexicon_python-0.3.0.dist-info/METADATA +393 -0
- lexicon_python-0.3.0.dist-info/RECORD +24 -0
- {lexicon_python-0.1.2.dist-info → lexicon_python-0.3.0.dist-info}/WHEEL +1 -1
- lexicon/lexicon.py +0 -766
- lexicon_python-0.1.2.dist-info/METADATA +0 -95
- lexicon_python-0.1.2.dist-info/RECORD +0 -7
- {lexicon_python-0.1.2.dist-info → lexicon_python-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {lexicon_python-0.1.2.dist-info → lexicon_python-0.3.0.dist-info}/top_level.txt +0 -0
lexicon/__init__.py
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
"""Public package surface for the lexicon Python client."""
|
|
2
2
|
|
|
3
|
-
from .
|
|
3
|
+
from .client import DEFAULT_HOST, LEXICON_PORT, Lexicon
|
|
4
|
+
from .resources.playlists_types import PlaylistResponse
|
|
5
|
+
from .resources.tag_categories_types import TagCategoryResponse
|
|
6
|
+
from .resources.tags_types import TagResponse
|
|
7
|
+
from .resources.tracks_types import (
|
|
8
|
+
CuePointResponse,
|
|
9
|
+
TempoMarkerResponse,
|
|
10
|
+
TrackResponse,
|
|
11
|
+
)
|
|
4
12
|
|
|
5
|
-
__all__ = [
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DEFAULT_HOST",
|
|
15
|
+
"LEXICON_PORT",
|
|
16
|
+
"Lexicon",
|
|
17
|
+
"TrackResponse",
|
|
18
|
+
"PlaylistResponse",
|
|
19
|
+
"TagResponse",
|
|
20
|
+
"TagCategoryResponse",
|
|
21
|
+
"CuePointResponse",
|
|
22
|
+
"TempoMarkerResponse",
|
|
23
|
+
]
|
lexicon/client.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Core Lexicon client with a raw-request escape hatch."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from .resources.playlist_tracks import PlaylistTracks
|
|
12
|
+
from .resources.playlists import Playlists
|
|
13
|
+
from .resources.tag_categories import TagCategories
|
|
14
|
+
from .resources.tags import Tags
|
|
15
|
+
from .resources.tracks import Tracks
|
|
16
|
+
from .tools import playlists as playlist_tools
|
|
17
|
+
from .tools import tracks as track_tools
|
|
18
|
+
|
|
19
|
+
DEFAULT_HOST = os.environ.get("LEXICON_HOST", "localhost")
|
|
20
|
+
LEXICON_PORT = int(os.environ.get("LEXICON_PORT", "48624"))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Tools:
|
|
24
|
+
"""Namespace for helper tools."""
|
|
25
|
+
|
|
26
|
+
playlists = playlist_tools
|
|
27
|
+
tracks = track_tools
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Lexicon:
|
|
31
|
+
"""Resource-grouped client for the Lexicon Local API."""
|
|
32
|
+
|
|
33
|
+
tracks: Tracks
|
|
34
|
+
playlists: Playlists
|
|
35
|
+
tags: Tags
|
|
36
|
+
tools: Tools
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
host: Optional[str] = None,
|
|
42
|
+
port: Optional[int | str] = None,
|
|
43
|
+
default_timeout: int = 20,
|
|
44
|
+
session: Optional[requests.Session] = None,
|
|
45
|
+
raise_on_error: bool = False,
|
|
46
|
+
raw_enums: bool = True,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Create a Lexicon client bound to an API instance.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
host
|
|
53
|
+
Hostname or IP for the Lexicon API.
|
|
54
|
+
port
|
|
55
|
+
API port number.
|
|
56
|
+
default_timeout
|
|
57
|
+
Default request timeout in seconds.
|
|
58
|
+
session
|
|
59
|
+
Optional requests session to reuse connections.
|
|
60
|
+
raise_on_error
|
|
61
|
+
If True, raise HTTP errors instead of returning None.
|
|
62
|
+
raw_enums
|
|
63
|
+
If True (default), return API enum values as raw codes (e.g. ``"1"``).
|
|
64
|
+
If False, convert to human-readable names (e.g. ``"normal"``).
|
|
65
|
+
"""
|
|
66
|
+
self.host = host or DEFAULT_HOST
|
|
67
|
+
self.port = int(port or LEXICON_PORT)
|
|
68
|
+
self.default_timeout = default_timeout
|
|
69
|
+
self.raise_on_error = raise_on_error
|
|
70
|
+
self.raw_enums = raw_enums
|
|
71
|
+
self._logger = logging.getLogger(__name__)
|
|
72
|
+
self._session = session
|
|
73
|
+
|
|
74
|
+
self.tracks: Tracks = Tracks(self)
|
|
75
|
+
self.playlists: Playlists = Playlists(self)
|
|
76
|
+
self.playlists.tracks = PlaylistTracks(
|
|
77
|
+
self, tracks=self.tracks, playlists=self.playlists
|
|
78
|
+
)
|
|
79
|
+
self.tags: Tags = Tags(self)
|
|
80
|
+
self.tags.categories = TagCategories(self)
|
|
81
|
+
self.tools = Tools()
|
|
82
|
+
|
|
83
|
+
def request(
|
|
84
|
+
self,
|
|
85
|
+
method: str,
|
|
86
|
+
path: str,
|
|
87
|
+
*,
|
|
88
|
+
params: Optional[dict[str, Any] | list[tuple[str, Any]]] = None,
|
|
89
|
+
json: Optional[dict[str, Any]] = None,
|
|
90
|
+
timeout: Optional[int] = None,
|
|
91
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
92
|
+
"""Send a raw request to the Lexicon API.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
method
|
|
97
|
+
HTTP method (GET, POST, PATCH, DELETE).
|
|
98
|
+
path
|
|
99
|
+
Endpoint path, with or without a leading `/v1`.
|
|
100
|
+
params
|
|
101
|
+
Query parameters for the request.
|
|
102
|
+
json
|
|
103
|
+
JSON payload for the request.
|
|
104
|
+
timeout
|
|
105
|
+
Timeout in seconds for this request.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
dict | list | None
|
|
110
|
+
Parsed JSON payload, or None if the response is empty or non-JSON.
|
|
111
|
+
"""
|
|
112
|
+
if not path.startswith("/"):
|
|
113
|
+
path = "/" + path
|
|
114
|
+
if not path.startswith("/v1/"):
|
|
115
|
+
path = "/v1" + path
|
|
116
|
+
url = f"http://{self.host}:{self.port}{path}"
|
|
117
|
+
|
|
118
|
+
requester = self._session or requests
|
|
119
|
+
try:
|
|
120
|
+
response = requester.request(
|
|
121
|
+
method,
|
|
122
|
+
url,
|
|
123
|
+
params=params,
|
|
124
|
+
json=json,
|
|
125
|
+
timeout=timeout or self.default_timeout,
|
|
126
|
+
)
|
|
127
|
+
response.raise_for_status()
|
|
128
|
+
except requests.HTTPError as exc:
|
|
129
|
+
if self.raise_on_error:
|
|
130
|
+
raise
|
|
131
|
+
# Extract error message from response body if available
|
|
132
|
+
error_msg = str(exc)
|
|
133
|
+
try:
|
|
134
|
+
error_body = response.json()
|
|
135
|
+
if isinstance(error_body, dict):
|
|
136
|
+
# Try common error message fields
|
|
137
|
+
if "message" in error_body:
|
|
138
|
+
error_msg = f"{exc}\nServer message: {error_body['message']}"
|
|
139
|
+
elif "error" in error_body:
|
|
140
|
+
error_msg = f"{exc}\nServer error: {error_body['error']}"
|
|
141
|
+
elif "detail" in error_body:
|
|
142
|
+
error_msg = f"{exc}\nDetails: {error_body['detail']}"
|
|
143
|
+
except (ValueError, AttributeError, KeyError):
|
|
144
|
+
pass # Response wasn't JSON or didn't have expected fields
|
|
145
|
+
self._logger.warning("Request failed for %s %s: %s", method, url, error_msg)
|
|
146
|
+
return None
|
|
147
|
+
except Exception as exc: # noqa: BLE001 - surface request failures
|
|
148
|
+
if self.raise_on_error:
|
|
149
|
+
raise
|
|
150
|
+
self._logger.warning("Request failed for %s %s: %s", method, url, exc)
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
if not response.content:
|
|
154
|
+
return None
|
|
155
|
+
try:
|
|
156
|
+
payload = response.json()
|
|
157
|
+
except ValueError: # noqa: PERF203 - only attempt JSON when present
|
|
158
|
+
self._logger.warning("Response from %s %s was not JSON", method, url)
|
|
159
|
+
return None
|
|
160
|
+
if isinstance(payload, (dict, list)):
|
|
161
|
+
return payload
|
|
162
|
+
return None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Resource module exports."""
|
|
2
|
+
|
|
3
|
+
from .playlist_tracks import PlaylistTracks
|
|
4
|
+
from .playlists import Playlists
|
|
5
|
+
from .tag_categories import TagCategories
|
|
6
|
+
from .tags import Tags
|
|
7
|
+
from .tracks import Tracks
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"PlaylistTracks",
|
|
11
|
+
"Playlists",
|
|
12
|
+
"TagCategories",
|
|
13
|
+
"Tags",
|
|
14
|
+
"Tracks",
|
|
15
|
+
]
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Shared types and validation helpers for resources.
|
|
2
|
+
|
|
3
|
+
This module contains:
|
|
4
|
+
- Validation mode type (shared across all resources)
|
|
5
|
+
- Color type definitions and normalization (used by tag_categories)
|
|
6
|
+
- Common validation normalizers (ID sequences, etc.)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import re
|
|
13
|
+
from typing import Literal, Sequence, get_args
|
|
14
|
+
|
|
15
|
+
from ..utils import unique_in_order
|
|
16
|
+
|
|
17
|
+
_logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# --- Shared Validation Mode --- #
|
|
20
|
+
ValidationMode = Literal["off", "warn", "strict"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# --- Color Types and Normalization --- #
|
|
24
|
+
Color = Literal[
|
|
25
|
+
"red_dark",
|
|
26
|
+
"red",
|
|
27
|
+
"red_light",
|
|
28
|
+
"red_orange",
|
|
29
|
+
"orange",
|
|
30
|
+
"beige",
|
|
31
|
+
"yellow_dark",
|
|
32
|
+
"yellow",
|
|
33
|
+
"lime",
|
|
34
|
+
"green_light",
|
|
35
|
+
"green",
|
|
36
|
+
"green_dark",
|
|
37
|
+
"teal",
|
|
38
|
+
"aqua",
|
|
39
|
+
"aqua_dark",
|
|
40
|
+
"blue_light",
|
|
41
|
+
"blue",
|
|
42
|
+
"blue_dark",
|
|
43
|
+
"blue_violet",
|
|
44
|
+
"violet",
|
|
45
|
+
"violet_light",
|
|
46
|
+
"magenta",
|
|
47
|
+
"magenta_dark",
|
|
48
|
+
"magenta_red",
|
|
49
|
+
"grey_light",
|
|
50
|
+
"grey_dark",
|
|
51
|
+
"black",
|
|
52
|
+
"white",
|
|
53
|
+
]
|
|
54
|
+
COLORS: tuple[Color, ...] = get_args(Color)
|
|
55
|
+
COLOR_RGBS: tuple[tuple[int, int, int], ...] = (
|
|
56
|
+
(158, 15, 7), # red_dark
|
|
57
|
+
(230, 15, 13), # red
|
|
58
|
+
(242, 102, 92), # red_light
|
|
59
|
+
(239, 89, 15), # red_orange
|
|
60
|
+
(232, 137, 20), # orange
|
|
61
|
+
(255, 225, 148), # beige
|
|
62
|
+
(245, 208, 1), # yellow_dark
|
|
63
|
+
(245, 245, 10), # yellow
|
|
64
|
+
(186, 232, 22), # lime
|
|
65
|
+
(174, 245, 95), # green_light
|
|
66
|
+
(127, 231, 16), # green
|
|
67
|
+
(75, 140, 8), # green_dark
|
|
68
|
+
(20, 222, 120), # teal
|
|
69
|
+
(20, 222, 202), # aqua
|
|
70
|
+
(7, 148, 134), # aqua_dark
|
|
71
|
+
(47, 168, 237), # blue_light
|
|
72
|
+
(14, 88, 222), # blue
|
|
73
|
+
(0, 40, 171), # blue_dark
|
|
74
|
+
(132, 0, 255), # blue_violet
|
|
75
|
+
(170, 59, 255), # violet
|
|
76
|
+
(198, 140, 243), # violet_light
|
|
77
|
+
(230, 15, 222), # magenta
|
|
78
|
+
(170, 0, 188), # magenta_dark
|
|
79
|
+
(222, 17, 92), # magenta_red
|
|
80
|
+
(173, 173, 173), # grey_light
|
|
81
|
+
(92, 92, 92), # grey_dark
|
|
82
|
+
(48, 48, 48), # black
|
|
83
|
+
(255, 255, 255), # white
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _parse_color_rgb(value: object) -> tuple[int, int, int] | None:
|
|
88
|
+
"""Parse a color input into an RGB tuple.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
value
|
|
93
|
+
Color input. Supported forms:
|
|
94
|
+
- ``None`` or the string ``"None"`` (case-insensitive) -> returns None
|
|
95
|
+
- Lexicon color names (see ``COLORS``) -> looked up from COLOR_RGBS
|
|
96
|
+
- Hex strings (``#RGB``, ``#RGBA``, ``#RRGGBB``, ``#RRGGBBAA``)
|
|
97
|
+
- RGB/RGBA tuples or lists (ints 0-255 or floats 0-1)
|
|
98
|
+
- Packed RGB integer (``0xRRGGBB``, ``0xAARRGGBB``)
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
tuple[int, int, int] or None
|
|
103
|
+
RGB tuple (0-255), or ``None`` when the input is ``None``.
|
|
104
|
+
|
|
105
|
+
Raises
|
|
106
|
+
------
|
|
107
|
+
ValueError
|
|
108
|
+
If the input cannot be parsed as a color value.
|
|
109
|
+
"""
|
|
110
|
+
if value is None:
|
|
111
|
+
return None
|
|
112
|
+
if isinstance(value, str):
|
|
113
|
+
if value.strip().lower() == "none":
|
|
114
|
+
return None
|
|
115
|
+
if value in COLORS:
|
|
116
|
+
return COLOR_RGBS[COLORS.index(value)]
|
|
117
|
+
hex_match = re.match(
|
|
118
|
+
r"^\s*#?([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})\s*$",
|
|
119
|
+
value,
|
|
120
|
+
flags=re.IGNORECASE,
|
|
121
|
+
)
|
|
122
|
+
if hex_match:
|
|
123
|
+
hex_value = hex_match.group(1)
|
|
124
|
+
if len(hex_value) in (3, 4):
|
|
125
|
+
hex_value = "".join(ch * 2 for ch in hex_value)
|
|
126
|
+
if len(hex_value) == 8:
|
|
127
|
+
hex_value = hex_value[:6]
|
|
128
|
+
return (
|
|
129
|
+
int(hex_value[0:2], 16),
|
|
130
|
+
int(hex_value[2:4], 16),
|
|
131
|
+
int(hex_value[4:6], 16),
|
|
132
|
+
)
|
|
133
|
+
raise ValueError(f"Unsupported string input {value!r}")
|
|
134
|
+
|
|
135
|
+
if isinstance(value, int):
|
|
136
|
+
if value < 0:
|
|
137
|
+
raise ValueError(f"Negative packed int {value!r}")
|
|
138
|
+
if value > 0xFFFFFF:
|
|
139
|
+
value = value & 0xFFFFFF
|
|
140
|
+
return ((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF)
|
|
141
|
+
|
|
142
|
+
if isinstance(value, (tuple, list)) and len(value) in (3, 4):
|
|
143
|
+
rgb_values = value[:3]
|
|
144
|
+
if all(isinstance(channel, (int, float)) for channel in rgb_values):
|
|
145
|
+
rgb = []
|
|
146
|
+
for channel in rgb_values:
|
|
147
|
+
channel_value = float(channel)
|
|
148
|
+
if channel_value <= 1:
|
|
149
|
+
channel_value *= 255
|
|
150
|
+
rgb.append(int(max(0, min(255, round(channel_value)))))
|
|
151
|
+
return (rgb[0], rgb[1], rgb[2])
|
|
152
|
+
raise ValueError(f"Invalid RGB tuple values {value!r}")
|
|
153
|
+
|
|
154
|
+
raise ValueError(f"Unsupported input type {type(value)}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _normalize_color(value: object) -> Color | None:
|
|
158
|
+
"""Normalize color inputs to the nearest Lexicon color name.
|
|
159
|
+
|
|
160
|
+
Used for tracks and cuepoints which use a fixed color swatch.
|
|
161
|
+
"""
|
|
162
|
+
rgb = _parse_color_rgb(value)
|
|
163
|
+
if rgb is None:
|
|
164
|
+
return None
|
|
165
|
+
return _nearest_color(rgb)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _normalize_color_hex(value: object) -> str | None:
|
|
169
|
+
"""Normalize color inputs to a hex string (#RRGGBB).
|
|
170
|
+
|
|
171
|
+
Used for tag categories which support a full color picker.
|
|
172
|
+
"""
|
|
173
|
+
rgb = _parse_color_rgb(value)
|
|
174
|
+
if rgb is None:
|
|
175
|
+
return None
|
|
176
|
+
return f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _nearest_color(rgb: tuple[int, int, int]) -> Color:
|
|
180
|
+
"""Find the nearest Lexicon color to the given RGB values."""
|
|
181
|
+
best_index = 0
|
|
182
|
+
best_distance = float("inf")
|
|
183
|
+
for index, candidate in enumerate(COLOR_RGBS):
|
|
184
|
+
dr = rgb[0] - candidate[0]
|
|
185
|
+
dg = rgb[1] - candidate[1]
|
|
186
|
+
db = rgb[2] - candidate[2]
|
|
187
|
+
distance = dr * dr + dg * dg + db * db
|
|
188
|
+
if distance < best_distance:
|
|
189
|
+
best_distance = distance
|
|
190
|
+
best_index = index
|
|
191
|
+
return COLORS[best_index]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# --- ID Sequence Normalization --- #
|
|
195
|
+
def _normalize_id_sequence(ids: int | Sequence[int] | object) -> list[int] | None:
|
|
196
|
+
"""Normalize single ID or sequence of IDs to a deduplicated list.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
ids
|
|
201
|
+
Single integer ID or sequence of integer IDs.
|
|
202
|
+
|
|
203
|
+
Returns
|
|
204
|
+
-------
|
|
205
|
+
list[int] | None
|
|
206
|
+
Deduplicated list of valid IDs (>= 1), or None if:
|
|
207
|
+
- Input is not int or sequence (or is str/bytes)
|
|
208
|
+
- No valid IDs found (all < 1)
|
|
209
|
+
|
|
210
|
+
Notes
|
|
211
|
+
-----
|
|
212
|
+
Used for normalizing track IDs, tag IDs, and similar integer sequences.
|
|
213
|
+
Filters out any non-integer elements and values < 1, then deduplicates.
|
|
214
|
+
"""
|
|
215
|
+
if isinstance(ids, int):
|
|
216
|
+
id_list = [ids]
|
|
217
|
+
elif isinstance(ids, Sequence) and not isinstance(ids, (str, bytes)):
|
|
218
|
+
id_list = list(ids)
|
|
219
|
+
else:
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
valid_ids = [
|
|
223
|
+
id_val for id_val in id_list if isinstance(id_val, int) and id_val >= 1
|
|
224
|
+
]
|
|
225
|
+
if not valid_ids:
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
return unique_in_order(valid_ids)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Base resource helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
8
|
+
from ..client import Lexicon
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Resource:
|
|
12
|
+
"""Shared helpers for resource classes."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client: "Lexicon") -> None:
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def _logger(self):
|
|
19
|
+
return self._client._logger
|
|
20
|
+
|
|
21
|
+
def _request(
|
|
22
|
+
self,
|
|
23
|
+
method: str,
|
|
24
|
+
path: str,
|
|
25
|
+
*,
|
|
26
|
+
params: Optional[dict[str, Any] | list[tuple[str, Any]]] = None,
|
|
27
|
+
json: Optional[dict[str, Any]] = None,
|
|
28
|
+
timeout: Optional[int] = None,
|
|
29
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
30
|
+
return self._client.request(
|
|
31
|
+
method, path, params=params, json=json, timeout=timeout
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get(
|
|
35
|
+
self,
|
|
36
|
+
path: str,
|
|
37
|
+
*,
|
|
38
|
+
params: Optional[dict[str, Any] | list[tuple[str, Any]]] = None,
|
|
39
|
+
timeout: Optional[int] = None,
|
|
40
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
41
|
+
return self._request("GET", path, params=params, timeout=timeout)
|
|
42
|
+
|
|
43
|
+
def _post(
|
|
44
|
+
self,
|
|
45
|
+
path: str,
|
|
46
|
+
*,
|
|
47
|
+
json: Optional[dict[str, Any]] = None,
|
|
48
|
+
timeout: Optional[int] = None,
|
|
49
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
50
|
+
return self._request("POST", path, json=json, timeout=timeout)
|
|
51
|
+
|
|
52
|
+
def _patch(
|
|
53
|
+
self,
|
|
54
|
+
path: str,
|
|
55
|
+
*,
|
|
56
|
+
json: Optional[dict[str, Any]] = None,
|
|
57
|
+
timeout: Optional[int] = None,
|
|
58
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
59
|
+
return self._request("PATCH", path, json=json, timeout=timeout)
|
|
60
|
+
|
|
61
|
+
def _delete(
|
|
62
|
+
self,
|
|
63
|
+
path: str,
|
|
64
|
+
*,
|
|
65
|
+
params: Optional[dict[str, Any] | list[tuple[str, Any]]] = None,
|
|
66
|
+
json: Optional[dict[str, Any]] = None,
|
|
67
|
+
timeout: Optional[int] = None,
|
|
68
|
+
) -> Optional[dict[str, Any] | list[Any]]:
|
|
69
|
+
return self._request("DELETE", path, params=params, json=json, timeout=timeout)
|