PyHiveLMS 5.12.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.
Files changed (45) hide show
  1. pyhive/__init__.py +13 -0
  2. pyhive/client.py +355 -0
  3. pyhive/src/__init__.py +0 -0
  4. pyhive/src/authenticated_hive_client.py +250 -0
  5. pyhive/src/types/__init__.py +0 -0
  6. pyhive/src/types/assignment.py +192 -0
  7. pyhive/src/types/class_.py +113 -0
  8. pyhive/src/types/common.py +56 -0
  9. pyhive/src/types/core_item.py +22 -0
  10. pyhive/src/types/enums/__init__.py +0 -0
  11. pyhive/src/types/enums/action_enum.py +18 -0
  12. pyhive/src/types/enums/assignment_status_enum.py +17 -0
  13. pyhive/src/types/enums/class_type_enum.py +13 -0
  14. pyhive/src/types/enums/clearance_enum.py +16 -0
  15. pyhive/src/types/enums/event_type_enum.py +14 -0
  16. pyhive/src/types/enums/exercise_patbas_enum.py +15 -0
  17. pyhive/src/types/enums/exercise_preview_types.py +15 -0
  18. pyhive/src/types/enums/form_field_type_enum.py +15 -0
  19. pyhive/src/types/enums/gender_enum.py +14 -0
  20. pyhive/src/types/enums/help_response_type_enum.py +14 -0
  21. pyhive/src/types/enums/help_status_enum.py +13 -0
  22. pyhive/src/types/enums/help_type_enum.py +18 -0
  23. pyhive/src/types/enums/queue_rule_enum.py +15 -0
  24. pyhive/src/types/enums/status_enum.py +21 -0
  25. pyhive/src/types/enums/sync_status_enum.py +15 -0
  26. pyhive/src/types/enums/visibility_enum.py +14 -0
  27. pyhive/src/types/event.py +140 -0
  28. pyhive/src/types/event_attendees_type_0_item.py +69 -0
  29. pyhive/src/types/event_color.py +63 -0
  30. pyhive/src/types/exercise.py +192 -0
  31. pyhive/src/types/form_field.py +149 -0
  32. pyhive/src/types/help_.py +275 -0
  33. pyhive/src/types/help_response.py +113 -0
  34. pyhive/src/types/help_response_segel_nested.py +129 -0
  35. pyhive/src/types/module.py +107 -0
  36. pyhive/src/types/notification_nested.py +80 -0
  37. pyhive/src/types/program.py +172 -0
  38. pyhive/src/types/queue.py +150 -0
  39. pyhive/src/types/queue_item.py +88 -0
  40. pyhive/src/types/subject.py +116 -0
  41. pyhive/src/types/tag.py +62 -0
  42. pyhive/src/types/user.py +375 -0
  43. pyhivelms-5.12.0.dist-info/METADATA +92 -0
  44. pyhivelms-5.12.0.dist-info/RECORD +45 -0
  45. pyhivelms-5.12.0.dist-info/WHEEL +4 -0
pyhive/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """Public package for the pyhive distribution.
2
+
3
+ Expose the public convenience symbol `HiveClient` at package level so users
4
+ can do `from pyhive import HiveClient`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ # Import the implementation from the `pyhive` package (implementation
10
+ # lives there) and expose the client at package level.
11
+ from pyhive.client import HiveClient # re-export
12
+
13
+ __all__ = ["HiveClient"]
pyhive/client.py ADDED
@@ -0,0 +1,355 @@
1
+ """High-level Hive API client.
2
+
3
+ This module provides ``HiveClient``, a small, synchronous authenticated
4
+ client for the Hive service. It exposes convenience methods that return
5
+ typed model objects from :mod:`src.types` and generator-based list
6
+ endpoints for memory-efficient iteration.
7
+ """
8
+
9
+ from collections.abc import Generator
10
+ from typing import TYPE_CHECKING, Optional, TypeVar, Any
11
+
12
+ import httpx
13
+
14
+ from src.authenticated_hive_client import _AuthenticatedHiveClient
15
+ from src.types.class_ import Class
16
+ from src.types.enums.class_type_enum import ClassTypeEnum
17
+ from src.types.exercise import Exercise
18
+ from src.types.form_field import FormField
19
+ from src.types.module import Module
20
+ from src.types.program import Program
21
+ from src.types.subject import Subject
22
+ from src.types.user import User
23
+
24
+ if TYPE_CHECKING:
25
+ from src.types.core_item import HiveCoreItem
26
+
27
+ CoreItemTypeT = TypeVar("CoreItemTypeT", bound="HiveCoreItem")
28
+
29
+
30
+ class HiveClient(_AuthenticatedHiveClient):
31
+ """HTTP client for accessing Hive API.
32
+
33
+ The client is used as a context manager and provides typed helpers for
34
+ common Hive resources (programs, subjects, modules, exercises, users,
35
+ classes, and form fields).
36
+ """
37
+
38
+ def __enter__(self) -> "HiveClient":
39
+ """Enter context manager and return this client instance.
40
+
41
+ This delegates to the base class context manager which manages the
42
+ underlying :class:`httpx.Client` session.
43
+ """
44
+ super().__enter__()
45
+ return self
46
+
47
+ def get_course_programs(
48
+ self,
49
+ id__in: Optional[list[int]] = None,
50
+ ) -> Generator[Program]:
51
+ """Yield :class:`Program` objects.
52
+
53
+ Args:
54
+ id__in: Optional list of program ids to filter the results.
55
+
56
+ Yields:
57
+ Program instances parsed from the API response.
58
+ """
59
+
60
+ query_params = httpx.QueryParams()
61
+ if id__in is not None:
62
+ query_params.set("id__in", id__in)
63
+ return (
64
+ Program.from_dict(program_dict, hive_client=self)
65
+ for program_dict in super().get(
66
+ "/api/core/course/programs/",
67
+ params=query_params,
68
+ )
69
+ )
70
+
71
+ def get_program(self, program_id: int) -> Program:
72
+ """Return a single :class:`Program` by id.
73
+
74
+ Args:
75
+ program_id: The program identifier.
76
+
77
+ Returns:
78
+ A populated :class:`Program` object.
79
+ """
80
+ return Program.from_dict(
81
+ super().get(f"/api/core/course/programs/{program_id}/"),
82
+ hive_client=self,
83
+ )
84
+
85
+ def get_course_subjects(
86
+ self,
87
+ parent_program__id__in: Optional[list[int]] = None,
88
+ ) -> Generator[Subject]:
89
+ """Yield :class:`Subject` objects for course subjects.
90
+
91
+ Args:
92
+ parent_program__id__in: Optional list of parent program ids to
93
+ filter subjects.
94
+
95
+ Yields:
96
+ Subject instances.
97
+ """
98
+
99
+ query_params = httpx.QueryParams()
100
+ if parent_program__id__in is not None:
101
+ query_params.set("parent_program__id__in", parent_program__id__in)
102
+ return (
103
+ Subject.from_dict(subject_dict, hive_client=self)
104
+ for subject_dict in super().get(
105
+ "/api/core/course/subjects/",
106
+ params=query_params,
107
+ )
108
+ )
109
+
110
+ def get_subject(self, subject_id: int) -> Subject:
111
+ """Return a single :class:`Subject` by id.
112
+
113
+ Args:
114
+ subject_id: The subject identifier.
115
+
116
+ Returns:
117
+ A populated :class:`Subject` object.
118
+ """
119
+ return Subject.from_dict(
120
+ super().get(f"/api/core/course/subjects/{subject_id}/"),
121
+ hive_client=self,
122
+ )
123
+
124
+ def get_course_modules(
125
+ self,
126
+ /,
127
+ parent_subject__id: Optional[int] = None,
128
+ parent_subject__parent_program__id__in: Optional[list[int]] = None,
129
+ ) -> Generator[Module]:
130
+ """Yield :class:`Module` objects for course modules.
131
+
132
+ Args:
133
+ parent_subject__id: Optional subject id to restrict modules.
134
+ parent_subject__parent_program__id__in: Optional list of program
135
+ ids to restrict modules.
136
+
137
+ Yields:
138
+ Module instances.
139
+ """
140
+
141
+ query_params = httpx.QueryParams()
142
+ if parent_subject__parent_program__id__in is not None:
143
+ query_params.set(
144
+ "parent_subject__parent_program__id__in",
145
+ parent_subject__parent_program__id__in,
146
+ )
147
+ if parent_subject__id is not None:
148
+ query_params.set("parent_subject__id", parent_subject__id)
149
+
150
+ return (
151
+ Module.from_dict(subject_dict, hive_client=self)
152
+ for subject_dict in super().get(
153
+ "/api/core/course/modules/",
154
+ params=query_params,
155
+ )
156
+ )
157
+
158
+ def get_module(self, module_id: int) -> Module:
159
+ """Return a single :class:`Module` by id.
160
+
161
+ Args:
162
+ module_id: The module identifier.
163
+
164
+ Returns:
165
+ A populated :class:`Module` object.
166
+ """
167
+ return Module.from_dict(
168
+ super().get(f"/api/core/course/modules/{module_id}/"),
169
+ hive_client=self,
170
+ )
171
+
172
+ def get_exercises( # pylint: disable=too-many-arguments
173
+ self,
174
+ *,
175
+ parent_module__id: Optional[int] = None,
176
+ parent_module__parent_subject__id: Optional[int] = None,
177
+ parent_module__parent_subject__parent_program__id__in: Optional[
178
+ list[int]
179
+ ] = None,
180
+ queue__id: Optional[int] = None,
181
+ tags__id__in: Optional[list[int]] = None,
182
+ ):
183
+ """Yield :class:`Exercise` objects.
184
+
185
+ Accepts common filtering keyword args which are forwarded to the
186
+ underlying list endpoint.
187
+ """
188
+
189
+ return self._get_core_items(
190
+ "/api/core/course/exercises/",
191
+ Exercise,
192
+ parent_module__id=parent_module__id,
193
+ parent_module__parent_subject__id=parent_module__parent_subject__id,
194
+ parent_module__parent_subject__parent_program__id__in=parent_module__parent_subject__parent_program__id__in,
195
+ queue__id=queue__id,
196
+ tags__id__in=tags__id__in,
197
+ )
198
+
199
+ def get_exercise(self, exercise_id: int) -> Exercise:
200
+ """Return a single :class:`Exercise` by id.
201
+
202
+ Args:
203
+ exercise_id: The exercise identifier.
204
+
205
+ Returns:
206
+ A populated :class:`Exercise` object.
207
+ """
208
+ return Exercise.from_dict(
209
+ super().get(f"/api/core/course/exercises/{exercise_id}/"),
210
+ hive_client=self,
211
+ )
212
+
213
+ def get_users( # pylint: disable=too-many-arguments
214
+ self,
215
+ *,
216
+ classes__id__in: Optional[list[int]] = None,
217
+ clearance__in: Optional[list[int]] = None,
218
+ id__in: Optional[list[int]] = None,
219
+ mentor__id: Optional[int] = None,
220
+ mentor__id__in: Optional[list[int]] = None,
221
+ program__id__in: Optional[list[int]] = None,
222
+ program_checker__id__in: Optional[list[int]] = None,
223
+ ) -> Generator[User]:
224
+ """Yield :class:`User` objects from the management users endpoint.
225
+
226
+ All kwargs are optional filters forwarded to the API.
227
+ """
228
+
229
+ return self._get_core_items(
230
+ "/api/core/management/users/",
231
+ User,
232
+ classes__id__in=classes__id__in,
233
+ clearance__in=clearance__in,
234
+ id__in=id__in,
235
+ mentor__id=mentor__id,
236
+ mentor__id__in=mentor__id__in,
237
+ program__id__in=program__id__in,
238
+ program_checker__id__in=program_checker__id__in,
239
+ )
240
+
241
+ def get_user(self, user_id: int) -> User:
242
+ """Return a single :class:`User` by id.
243
+
244
+ Args:
245
+ user_id: The user identifier.
246
+
247
+ Returns:
248
+ A populated :class:`User` object.
249
+ """
250
+ return User.from_dict(
251
+ super().get(f"/api/core/management/users/{user_id}/"),
252
+ hive_client=self,
253
+ )
254
+
255
+ def get_classes(
256
+ self,
257
+ *,
258
+ id__in: Optional[list[int]] = None,
259
+ name: Optional[str] = None,
260
+ program__id__in: Optional[list[int]] = None,
261
+ type_: Optional[ClassTypeEnum] = None,
262
+ ) -> Generator[Class]:
263
+ """Yield :class:`Class` objects from the management classes endpoint.
264
+
265
+ Filters may be provided as keyword arguments.
266
+ """
267
+
268
+ return self._get_core_items(
269
+ "/api/core/management/classes/",
270
+ Class,
271
+ id__in=id__in,
272
+ name=name,
273
+ program__id__in=program__id__in,
274
+ type_=type_,
275
+ )
276
+
277
+ def get_class(
278
+ self,
279
+ class_id: int,
280
+ ) -> Class:
281
+ """Return a single :class:`Class` by id.
282
+
283
+ Args:
284
+ class_id: The class identifier.
285
+
286
+ Returns:
287
+ A populated :class:`Class` object.
288
+ """
289
+ return Class.from_dict(
290
+ super().get(f"/api/core/management/classes/{class_id}/"),
291
+ hive_client=self,
292
+ )
293
+
294
+ def get_exercise_fields(
295
+ self,
296
+ exercise_id: int,
297
+ ) -> Generator[FormField]:
298
+ """Yield :class:`FormField` objects for an exercise.
299
+
300
+ Args:
301
+ exercise_id: The exercise identifier.
302
+ """
303
+
304
+ return self._get_core_items(
305
+ f"/api/core/course/exercises/{exercise_id}/fields/",
306
+ FormField,
307
+ exercise_id=exercise_id,
308
+ )
309
+
310
+ def get_exercise_field(
311
+ self,
312
+ exercise_id: int,
313
+ field_id: int,
314
+ ) -> FormField:
315
+ """Return a single :class:`FormField` for an exercise by id.
316
+
317
+ Args:
318
+ exercise_id: The exercise identifier.
319
+ field_id: The field identifier.
320
+
321
+ Returns:
322
+ A populated :class:`FormField` object.
323
+ """
324
+ return FormField.from_dict(
325
+ super().get(f"/api/core/course/exercises/{exercise_id}/fields/{field_id}/"),
326
+ hive_client=self,
327
+ )
328
+
329
+ def _get_core_items(
330
+ self,
331
+ endpoint: str,
332
+ item_type: type[CoreItemTypeT],
333
+ /,
334
+ **kwargs: dict[str, Any], # noqa: ANN401
335
+ ) -> Generator[CoreItemTypeT]:
336
+ """Internal helper to yield typed core items from a list endpoint.
337
+
338
+ Args:
339
+ endpoint: API endpoint path for the list resource.
340
+ item_type: Model class with a ``from_dict`` constructor.
341
+ **kwargs: Filter query parameters forwarded to the endpoint.
342
+
343
+ Yields:
344
+ Instances of ``item_type`` created via ``from_dict``.
345
+ """
346
+
347
+ query_params = httpx.QueryParams()
348
+ for name, value in kwargs.items():
349
+ if value is not None:
350
+ query_params = query_params.set(name, value)
351
+
352
+ return (
353
+ item_type.from_dict(x, hive_client=self)
354
+ for x in super().get(endpoint, params=query_params)
355
+ )
pyhive/src/__init__.py ADDED
File without changes
@@ -0,0 +1,250 @@
1
+ """Authentication helpers and a small authenticated HTTP client for Hive.
2
+
3
+ This module provides decorators that add retry and token-refresh behavior to
4
+ HTTP calls and an internal ``_AuthenticatedHiveClient`` which wraps an
5
+ :class:`httpx.Client` and handles login/refresh for the Hive API.
6
+ """
7
+
8
+ import functools
9
+ import time
10
+ from collections.abc import Callable
11
+ from types import TracebackType
12
+ from typing import Any, TypeVar, cast
13
+
14
+ import httpx
15
+
16
+ F = TypeVar("F", bound=Callable[..., httpx.Response])
17
+
18
+ MAX_RETRIES_ON_SERVER_ERRORS = 5
19
+ INITIAL_BACKOFF_SECONDS = 0.5
20
+
21
+
22
+ def _retry_on_bad_gateway(func: F) -> F:
23
+ """Decorator: retry a request when the server returns HTTP 502.
24
+
25
+ The wrapped function is expected to return an :class:`httpx.Response`.
26
+ Retries use exponential backoff and will re-raise the final response's
27
+ HTTP error if all retries fail.
28
+ """
29
+
30
+ @functools.wraps(func)
31
+ def wrapper(self: "_AuthenticatedHiveClient", *args: Any, **kwargs: Any):
32
+ delay = INITIAL_BACKOFF_SECONDS
33
+ if MAX_RETRIES_ON_SERVER_ERRORS <= 0:
34
+ raise ValueError("MAX_RETRIES_ON_SERVER_ERRORS must be greater than 0")
35
+ response = None
36
+ for attempt in range(MAX_RETRIES_ON_SERVER_ERRORS):
37
+ response = func(self, *args, **kwargs)
38
+ if response.status_code != httpx.codes.BAD_GATEWAY.value:
39
+ return response
40
+ if attempt < MAX_RETRIES_ON_SERVER_ERRORS - 1:
41
+ time.sleep(delay)
42
+ delay *= 2
43
+ assert response is not None
44
+ response.raise_for_status()
45
+ return response
46
+
47
+ return cast("F", wrapper)
48
+
49
+
50
+ def _refresh_token_on_unauthorized(func: F) -> F:
51
+ """Decorator: refresh access token and retry on HTTP 401 Unauthorized.
52
+
53
+ If the wrapped function returns a 401 status, the client's
54
+ ``_refresh_access_token`` is called and the request is retried once.
55
+ """
56
+
57
+ @functools.wraps(func)
58
+ def wrapper(self: "_AuthenticatedHiveClient", *args: Any, **kwargs: Any):
59
+ response = func(self, *args, **kwargs)
60
+ if response.status_code == httpx.codes.UNAUTHORIZED.value:
61
+ self._refresh_access_token() # pylint: disable=protected-access
62
+ response = func(self, *args, **kwargs)
63
+ response.raise_for_status()
64
+ return response
65
+
66
+ return cast("F", wrapper)
67
+
68
+
69
+ def _with_retries_and_token_refresh(func: F) -> F:
70
+ """Compose the retry and token-refresh decorators.
71
+
72
+ Use this to wrap HTTP methods so they automatically handle transient
73
+ 502 errors and expired access tokens.
74
+ """
75
+
76
+ return _refresh_token_on_unauthorized(_retry_on_bad_gateway(func))
77
+
78
+
79
+ class _AuthenticatedHiveClient:
80
+ """Internal class used to handle authentication and re-authentication with Hive web endpoint."""
81
+
82
+ _refresh_token: str
83
+ _access_token: str
84
+ _session: httpx.Client
85
+ username: str
86
+
87
+ def __init__( # pylint: disable=too-many-arguments
88
+ self,
89
+ username: str,
90
+ password: str,
91
+ hive_url: str,
92
+ *,
93
+ timeout: httpx.Timeout | float | None = None,
94
+ headers: dict[str, str] | None = None,
95
+ verify: bool | str | None = None,
96
+ **kwargs: Any,
97
+ ) -> None:
98
+ """Create an authenticated client.
99
+
100
+ Common HTTP client options may be provided explicitly (typed) or via
101
+ ``**kwargs`` and will be forwarded to :class:`httpx.Client`.
102
+
103
+ Typed kwargs provided (timeout, headers, verify) take precedence; the
104
+ rest are forwarded from ``kwargs``.
105
+ """
106
+ self.username = username
107
+ self.hive_url = hive_url
108
+
109
+ client_kwargs: dict[str, Any] = {}
110
+ if timeout is not None:
111
+ client_kwargs["timeout"] = timeout
112
+ if headers is not None:
113
+ client_kwargs["headers"] = headers
114
+ if verify is not None:
115
+ client_kwargs["verify"] = verify
116
+
117
+ # Include any other httpx.Client kwargs passed in **kwargs
118
+ client_kwargs.update(kwargs)
119
+
120
+ self._session = httpx.Client(
121
+ base_url=hive_url,
122
+ **client_kwargs,
123
+ ).__enter__()
124
+ self._login(username, password)
125
+
126
+ def __enter__(self) -> "_AuthenticatedHiveClient":
127
+ """Enter context manager and return this client instance.
128
+
129
+ The underlying :class:`httpx.Client` is managed by this object's
130
+ lifecycle; entering the context returns the authenticated client so
131
+ callers can perform API calls.
132
+ """
133
+
134
+ return self
135
+
136
+ def __exit__(
137
+ self,
138
+ type_: type[BaseException] | None,
139
+ value: BaseException | None,
140
+ traceback: TracebackType | None,
141
+ ) -> bool | None:
142
+ """Exit the context and close the underlying httpx session.
143
+
144
+ This delegates to the managed :class:`httpx.Client`'s ``__exit__``
145
+ method to ensure resources are released.
146
+ """
147
+
148
+ self._session.__exit__(type_, value, traceback)
149
+
150
+ def _login(self, username: str, password: str) -> None:
151
+ """Perform an authentication request and store access/refresh tokens.
152
+
153
+ This sets the ``Authorization`` header on the underlying session.
154
+ """
155
+
156
+ response = self._session.post(
157
+ "/api/core/token/",
158
+ json={"username": username, "password": password},
159
+ )
160
+ response.raise_for_status()
161
+ data = response.json()
162
+ self._access_token = data["access"]
163
+ self._refresh_token = data["refresh"]
164
+
165
+ self._session.headers.update({"Authorization": f"Bearer {self._access_token}"})
166
+
167
+ def _refresh_access_token(self) -> None:
168
+ """Refresh the access token using the stored refresh token.
169
+
170
+ Updates the stored access and refresh tokens and the session header.
171
+ """
172
+
173
+ response = self._session.post(
174
+ "/api/core/token/refresh/",
175
+ json={"refresh": self._refresh_token},
176
+ )
177
+ response.raise_for_status()
178
+ data = response.json()
179
+ self._access_token = data["access"]
180
+ self._refresh_token = data["refresh"]
181
+
182
+ self._session.headers.update({"Authorization": f"Bearer {self._access_token}"})
183
+
184
+ @_with_retries_and_token_refresh
185
+ def _get(
186
+ self, endpoint: str, params: httpx.QueryParams | None = None
187
+ ) -> httpx.Response:
188
+ """Low-level GET that returns an :class:`httpx.Response`.
189
+
190
+ This is decorated to handle retries and token refresh automatically.
191
+ """
192
+
193
+ return self._session.get(endpoint, params=params)
194
+
195
+ @_with_retries_and_token_refresh
196
+ def _post(self, endpoint: str, data: dict[Any, Any]) -> httpx.Response:
197
+ """Low-level POST that returns an :class:`httpx.Response` with JSON body.
198
+
199
+ The ``data`` is JSON-encoded into the request body.
200
+ """
201
+
202
+ return self._session.post(endpoint, json=data)
203
+
204
+ @_with_retries_and_token_refresh
205
+ def _patch(self, endpoint: str, data: dict[Any, Any]) -> httpx.Response:
206
+ """Low-level PATCH request; returns :class:`httpx.Response`.
207
+
208
+ The ``data`` is JSON-encoded into the request body.
209
+ """
210
+
211
+ return self._session.patch(endpoint, json=data)
212
+
213
+ @_with_retries_and_token_refresh
214
+ def _delete(self, endpoint: str) -> httpx.Response:
215
+ """Low-level DELETE request; returns :class:`httpx.Response`."""
216
+
217
+ return self._session.delete(endpoint)
218
+
219
+ @_with_retries_and_token_refresh
220
+ def _put(self, endpoint: str, data: dict[Any, Any]) -> httpx.Response:
221
+ """Low-level PUT request; returns :class:`httpx.Response`.
222
+
223
+ The ``data`` is JSON-encoded into the request body.
224
+ """
225
+
226
+ return self._session.put(endpoint, json=data)
227
+
228
+ def get(self, endpoint: str, params: httpx.QueryParams | None = None) -> Any:
229
+ """High-level GET that returns parsed JSON from the response.
230
+
231
+ This calls the decorated ``_get`` helper and returns its JSON body.
232
+ """
233
+
234
+ return self._get(endpoint, params).json()
235
+
236
+ def post(self, endpoint: str, data: dict[Any, Any]) -> Any:
237
+ """High-level POST that returns parsed JSON from the response.
238
+
239
+ The ``data`` dict is JSON-encoded for the request body.
240
+ """
241
+
242
+ return self._post(endpoint, data).json()
243
+
244
+ def __repr__(self) -> str:
245
+ """Return a short representation including username and hive_url.
246
+
247
+ The representation intentionally omits secrets.
248
+ """
249
+
250
+ return f"HiveClient({self.username!r}, input(), {self.hive_url!r})"
File without changes