sinatra-dev 0.1.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 (50) hide show
  1. sinatra_dev/__init__.py +71 -0
  2. sinatra_dev/_client.py +391 -0
  3. sinatra_dev/_errors.py +53 -0
  4. sinatra_dev/_generated/__init__.py +8 -0
  5. sinatra_dev/_generated/api/__init__.py +1 -0
  6. sinatra_dev/_generated/api/me/__init__.py +1 -0
  7. sinatra_dev/_generated/api/me/me_get.py +156 -0
  8. sinatra_dev/_generated/api/me/me_revoke_token.py +173 -0
  9. sinatra_dev/_generated/api/messages/__init__.py +1 -0
  10. sinatra_dev/_generated/api/messages/messages_create.py +323 -0
  11. sinatra_dev/_generated/api/messages/messages_list.py +332 -0
  12. sinatra_dev/_generated/api/tasks/__init__.py +1 -0
  13. sinatra_dev/_generated/api/tasks/tasks_create.py +337 -0
  14. sinatra_dev/_generated/api/tasks/tasks_get.py +202 -0
  15. sinatra_dev/_generated/api/tasks/tasks_list.py +252 -0
  16. sinatra_dev/_generated/api/tasks/tasks_stop.py +231 -0
  17. sinatra_dev/_generated/api/tickets/__init__.py +1 -0
  18. sinatra_dev/_generated/api/tickets/tickets_delegate.py +273 -0
  19. sinatra_dev/_generated/api/tickets/tickets_search.py +265 -0
  20. sinatra_dev/_generated/client.py +280 -0
  21. sinatra_dev/_generated/errors.py +16 -0
  22. sinatra_dev/_generated/models/__init__.py +47 -0
  23. sinatra_dev/_generated/models/create_message_request.py +62 -0
  24. sinatra_dev/_generated/models/create_message_response.py +62 -0
  25. sinatra_dev/_generated/models/create_task_request.py +106 -0
  26. sinatra_dev/_generated/models/create_task_response.py +76 -0
  27. sinatra_dev/_generated/models/delegate_github_ticket.py +86 -0
  28. sinatra_dev/_generated/models/delegate_linear_ticket.py +78 -0
  29. sinatra_dev/_generated/models/delegate_ticket_response.py +62 -0
  30. sinatra_dev/_generated/models/error.py +168 -0
  31. sinatra_dev/_generated/models/me.py +86 -0
  32. sinatra_dev/_generated/models/me_user.py +98 -0
  33. sinatra_dev/_generated/models/message.py +113 -0
  34. sinatra_dev/_generated/models/message_list.py +91 -0
  35. sinatra_dev/_generated/models/pull_request.py +70 -0
  36. sinatra_dev/_generated/models/stop_task_response.py +62 -0
  37. sinatra_dev/_generated/models/task.py +201 -0
  38. sinatra_dev/_generated/models/task_list.py +91 -0
  39. sinatra_dev/_generated/models/ticket.py +147 -0
  40. sinatra_dev/_generated/models/ticket_search_result.py +97 -0
  41. sinatra_dev/_generated/models/ticket_search_result_github.py +77 -0
  42. sinatra_dev/_generated/models/ticket_search_result_linear.py +79 -0
  43. sinatra_dev/_generated/models/workspace.py +78 -0
  44. sinatra_dev/_generated/types.py +54 -0
  45. sinatra_dev/_retry.py +72 -0
  46. sinatra_dev/py.typed +0 -0
  47. sinatra_dev-0.1.0.dist-info/METADATA +184 -0
  48. sinatra_dev-0.1.0.dist-info/RECORD +50 -0
  49. sinatra_dev-0.1.0.dist-info/WHEEL +4 -0
  50. sinatra_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,71 @@
1
+ """A Python client for the Sinatra API.
2
+
3
+ from sinatra_dev import Sinatra
4
+
5
+ with Sinatra() as sinatra: # reads SINATRA_TOKEN
6
+ me = sinatra.me.get()
7
+
8
+ Reference: https://docs.sinatra.dev/docs/api-reference
9
+ """
10
+
11
+ from ._client import (
12
+ DEFAULT,
13
+ DEFAULT_BASE_URL,
14
+ DEFAULT_TIMEOUT,
15
+ MeApi,
16
+ MessagesApi,
17
+ Sinatra,
18
+ TasksApi,
19
+ TicketsApi,
20
+ Timeout,
21
+ )
22
+ from ._errors import SinatraError
23
+ from ._generated.models import (
24
+ CreateMessageResponse,
25
+ CreateTaskResponse,
26
+ DelegateTicketResponse,
27
+ Me,
28
+ Message,
29
+ MessageList,
30
+ MeUser,
31
+ PullRequest,
32
+ StopTaskResponse,
33
+ Task,
34
+ TaskList,
35
+ Ticket,
36
+ TicketSearchResult,
37
+ TicketSearchResultGithub,
38
+ TicketSearchResultLinear,
39
+ Workspace,
40
+ )
41
+ from ._generated.models import Error as ErrorBody
42
+
43
+ __all__ = [
44
+ "DEFAULT",
45
+ "DEFAULT_BASE_URL",
46
+ "DEFAULT_TIMEOUT",
47
+ "CreateMessageResponse",
48
+ "CreateTaskResponse",
49
+ "DelegateTicketResponse",
50
+ "ErrorBody",
51
+ "Me",
52
+ "MeApi",
53
+ "MeUser",
54
+ "Message",
55
+ "MessageList",
56
+ "MessagesApi",
57
+ "PullRequest",
58
+ "Sinatra",
59
+ "SinatraError",
60
+ "StopTaskResponse",
61
+ "Task",
62
+ "TaskList",
63
+ "TasksApi",
64
+ "Ticket",
65
+ "TicketSearchResult",
66
+ "TicketSearchResultGithub",
67
+ "TicketSearchResultLinear",
68
+ "TicketsApi",
69
+ "Timeout",
70
+ "Workspace",
71
+ ]
sinatra_dev/_client.py ADDED
@@ -0,0 +1,391 @@
1
+ """The Sinatra client: transport, retries and timeouts around the generated code.
2
+
3
+ The generated package (``_generated``) builds each request (method, encoded
4
+ path, query, headers, JSON body) and decodes each response into its attrs
5
+ models. This module owns the ``httpx.Client`` and sends every request itself,
6
+ so it can retry with the same request and apply a timeout per call, which the
7
+ generated ``sync_detailed`` functions can't.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import uuid
14
+ from collections.abc import Callable, Iterator
15
+ from importlib.metadata import PackageNotFoundError, version
16
+ from types import TracebackType
17
+ from typing import Any, Final, Literal, TypeVar
18
+
19
+ import httpx
20
+
21
+ from . import _retry
22
+ from ._errors import SinatraError
23
+ from ._generated.api.me import me_get, me_revoke_token
24
+ from ._generated.api.messages import messages_create, messages_list
25
+ from ._generated.api.tasks import tasks_create, tasks_get, tasks_list, tasks_stop
26
+ from ._generated.api.tickets import tickets_delegate, tickets_search
27
+ from ._generated.models import (
28
+ CreateMessageRequest,
29
+ CreateMessageResponse,
30
+ CreateTaskRequest,
31
+ CreateTaskResponse,
32
+ DelegateGithubTicket,
33
+ DelegateLinearTicket,
34
+ DelegateTicketResponse,
35
+ Me,
36
+ Message,
37
+ MessageList,
38
+ StopTaskResponse,
39
+ Task,
40
+ TaskList,
41
+ TicketSearchResult,
42
+ )
43
+ from ._generated.types import UNSET, Unset
44
+
45
+ DEFAULT_BASE_URL: Final = "https://api.sinatra.dev"
46
+ DEFAULT_TIMEOUT: Final = 60.0
47
+ """Seconds, as an httpx timeout: for connecting, and for each wait for data."""
48
+
49
+ Timeout = float | httpx.Timeout | None
50
+ """Seconds (or an ``httpx.Timeout``); ``None`` for no timeout."""
51
+
52
+ T = TypeVar("T")
53
+
54
+ # Every key of a generated `_get_kwargs()` that `_Transport.send` passes on.
55
+ _REQUEST_KEYS: Final = frozenset({"method", "url", "params", "json", "headers"})
56
+
57
+
58
+ class _Default:
59
+ """The type of :data:`DEFAULT`: "use the client's timeout"."""
60
+
61
+ def __repr__(self) -> str:
62
+ return "DEFAULT"
63
+
64
+
65
+ DEFAULT: Final = _Default()
66
+
67
+
68
+ def _user_agent() -> str:
69
+ try:
70
+ return f"sinatra-dev-python/{version('sinatra-dev')}"
71
+ except PackageNotFoundError: # pragma: no cover - running from a source tree
72
+ return "sinatra-dev-python"
73
+
74
+
75
+ def _unset(value: T | None) -> T | Unset:
76
+ return UNSET if value is None else value
77
+
78
+
79
+ class Sinatra:
80
+ """A client for the Sinatra API.
81
+
82
+ Its namespaces mirror the API's operations: ``me.get`` is ``GET /v1/me``,
83
+ ``tasks.create`` is ``POST /v1/workspaces/{workspaceId}/tasks``.
84
+
85
+ Requests that are safe to repeat (GETs, the token DELETE, and starts and
86
+ messages, which always carry an ``Idempotency-Key``) are retried up to 3
87
+ times on ``429`` and ``503``. ``tasks.stop`` is retried on ``503`` only.
88
+ Error responses raise :class:`SinatraError`.
89
+
90
+ Use it as a context manager, or call :meth:`close`, to release its
91
+ connections.
92
+
93
+ Reference: https://docs.sinatra.dev/docs/api-reference
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ *,
99
+ token: str | None = None,
100
+ base_url: str | None = None,
101
+ timeout: Timeout = DEFAULT_TIMEOUT,
102
+ http_client: httpx.Client | None = None,
103
+ ) -> None:
104
+ """
105
+ Args:
106
+ token: An API token (``sntr_...``). Defaults to the
107
+ ``SINATRA_TOKEN`` environment variable.
108
+ base_url: Defaults to ``https://api.sinatra.dev``.
109
+ timeout: Seconds each attempt may wait to connect, and for each
110
+ chunk of the response, before httpx raises
111
+ ``httpx.TimeoutException``. Defaults to 60; ``None`` for no
112
+ timeout. A call can override it with its ``timeout`` argument.
113
+ http_client: An ``httpx.Client`` to send requests with, for
114
+ example one with a proxy. The client doesn't close it.
115
+ """
116
+ token = token or os.environ.get("SINATRA_TOKEN") or None
117
+ if not token:
118
+ raise ValueError("No Sinatra API token: pass token=... or set SINATRA_TOKEN.")
119
+ self._transport = _Transport(
120
+ http=http_client or httpx.Client(),
121
+ owns_http=http_client is None,
122
+ base_url=(base_url or DEFAULT_BASE_URL).rstrip("/"),
123
+ headers={"authorization": f"Bearer {token}", "user-agent": _user_agent()},
124
+ timeout=timeout,
125
+ )
126
+ self.me: Final = MeApi(self._transport)
127
+ self.tasks: Final = TasksApi(self._transport)
128
+ self.messages: Final = MessagesApi(self._transport)
129
+ self.tickets: Final = TicketsApi(self._transport)
130
+
131
+ def close(self) -> None:
132
+ """Closes the connections, unless the ``httpx.Client`` was yours."""
133
+ self._transport.close()
134
+
135
+ def __enter__(self) -> Sinatra:
136
+ return self
137
+
138
+ def __exit__(
139
+ self,
140
+ exc_type: type[BaseException] | None,
141
+ exc: BaseException | None,
142
+ traceback: TracebackType | None,
143
+ ) -> None:
144
+ self.close()
145
+
146
+
147
+ class _Transport:
148
+ """Sends a generated request with the client's auth, retries and timeout."""
149
+
150
+ def __init__(
151
+ self, *, http: httpx.Client, owns_http: bool, base_url: str, headers: dict[str, str], timeout: Timeout
152
+ ) -> None:
153
+ self.http = http
154
+ self.owns_http = owns_http
155
+ self.base_url = base_url
156
+ self.headers = headers
157
+ self.timeout = timeout
158
+
159
+ def close(self) -> None:
160
+ if self.owns_http:
161
+ self.http.close()
162
+
163
+ def send(self, request: dict[str, Any], retry_on: frozenset[int], timeout: Timeout | _Default) -> httpx.Response:
164
+ """Sends ``request`` (a generated ``_get_kwargs()``) and returns the 2xx
165
+ response. Raises :class:`SinatraError` for any other status."""
166
+ unknown = set(request) - _REQUEST_KEYS
167
+ if unknown:
168
+ # A generator change built a request with parts this client doesn't
169
+ # send (a file upload, raw content, cookies...). Refuse rather than
170
+ # silently send something else.
171
+ raise RuntimeError(
172
+ f"sinatra-dev can't send {request.get('method', '?').upper()} {request.get('url', '?')}: "
173
+ f"the generated request has parts it doesn't handle: {', '.join(sorted(unknown))}"
174
+ )
175
+ method: str = request["method"]
176
+ url = self.base_url + request["url"]
177
+ headers = {**request.get("headers", {}), **self.headers}
178
+ per_call = self.timeout if isinstance(timeout, _Default) else timeout
179
+
180
+ def attempt() -> httpx.Response:
181
+ return self.http.request(
182
+ method.upper(),
183
+ url,
184
+ params=request.get("params"),
185
+ json=request.get("json"),
186
+ headers=headers,
187
+ timeout=per_call,
188
+ )
189
+
190
+ response = _retry.with_retries(retry_on, attempt)
191
+ if not response.is_success:
192
+ raise SinatraError.from_response(response)
193
+ return response
194
+
195
+ def decode(
196
+ self,
197
+ request: dict[str, Any],
198
+ retry_on: frozenset[int],
199
+ timeout: Timeout | _Default,
200
+ model: Callable[[dict[str, Any]], T],
201
+ ) -> T:
202
+ return model(self.send(request, retry_on, timeout).json())
203
+
204
+
205
+ class MeApi:
206
+ """``me.*``: the token's user."""
207
+
208
+ def __init__(self, transport: _Transport) -> None:
209
+ self._t = transport
210
+
211
+ def get(self, *, timeout: Timeout | _Default = DEFAULT) -> Me:
212
+ """``me.get``: the user the token acts as, and their workspaces."""
213
+ return self._t.decode(me_get._get_kwargs(), _retry.RETRY_IDEMPOTENT, timeout, Me.from_dict)
214
+
215
+ def revoke_token(self, *, timeout: Timeout | _Default = DEFAULT) -> None:
216
+ """``me.revokeToken``: revokes the token this client sends. Later requests get ``401``."""
217
+ self._t.send(me_revoke_token._get_kwargs(), _retry.RETRY_IDEMPOTENT, timeout)
218
+
219
+
220
+ class TasksApi:
221
+ """``tasks.*``: start, follow and stop tasks."""
222
+
223
+ def __init__(self, transport: _Transport) -> None:
224
+ self._t = transport
225
+
226
+ def list(
227
+ self,
228
+ workspace_id: str,
229
+ *,
230
+ status: str | None = None,
231
+ limit: int | None = None,
232
+ cursor: str | None = None,
233
+ timeout: Timeout | _Default = DEFAULT,
234
+ ) -> TaskList:
235
+ """``tasks.list``: one page of the workspace's tasks, most recently active first."""
236
+ request = tasks_list._get_kwargs(
237
+ workspace_id, status=_unset(status), limit=_unset(limit), cursor=_unset(cursor)
238
+ )
239
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, TaskList.from_dict)
240
+
241
+ def list_all(
242
+ self,
243
+ workspace_id: str,
244
+ *,
245
+ status: str | None = None,
246
+ limit: int | None = None,
247
+ cursor: str | None = None,
248
+ timeout: Timeout | _Default = DEFAULT,
249
+ ) -> Iterator[Task]:
250
+ """Every task from ``cursor`` on (or from the start), following ``next_cursor``
251
+ page by page. ``limit`` is the page size."""
252
+ while True:
253
+ page = self.list(workspace_id, status=status, limit=limit, cursor=cursor, timeout=timeout)
254
+ yield from page.tasks
255
+ cursor = page.next_cursor
256
+ if not cursor:
257
+ return
258
+
259
+ def get(self, workspace_id: str, task_id: str, *, timeout: Timeout | _Default = DEFAULT) -> Task:
260
+ """``tasks.get``: one task, wherever it started."""
261
+ request = tasks_get._get_kwargs(workspace_id, task_id)
262
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, Task.from_dict)
263
+
264
+ def create(
265
+ self,
266
+ workspace_id: str,
267
+ prompt: str,
268
+ *,
269
+ title: str | None = None,
270
+ repo: str | None = None,
271
+ idempotency_key: str | None = None,
272
+ timeout: Timeout | _Default = DEFAULT,
273
+ ) -> CreateTaskResponse:
274
+ """``tasks.create``: starts a task. Returns once the task is made, not when
275
+ it's done: poll ``tasks.get`` and ``messages.list`` to follow it.
276
+
277
+ ``idempotency_key`` is 1 to 128 printable ASCII characters, with no
278
+ spaces. When you leave it out, the client makes a random UUID. Either
279
+ way, every retry of the call sends the same key, so a retried start
280
+ never starts a second task.
281
+ """
282
+ body = CreateTaskRequest(prompt=prompt, title=_unset(title), repo=_unset(repo))
283
+ key = idempotency_key if idempotency_key is not None else str(uuid.uuid4())
284
+ request = tasks_create._get_kwargs(workspace_id, body=body, idempotency_key=key)
285
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, CreateTaskResponse.from_dict)
286
+
287
+ def stop(self, workspace_id: str, task_id: str, *, timeout: Timeout | _Default = DEFAULT) -> StopTaskResponse:
288
+ """``tasks.stop``: stops the task's running turn. A task that isn't running is ``409 not_running``."""
289
+ request = tasks_stop._get_kwargs(workspace_id, task_id)
290
+ return self._t.decode(request, _retry.RETRY_UNAVAILABLE, timeout, StopTaskResponse.from_dict)
291
+
292
+
293
+ class MessagesApi:
294
+ """``messages.*``: a task's conversation."""
295
+
296
+ def __init__(self, transport: _Transport) -> None:
297
+ self._t = transport
298
+
299
+ def create(
300
+ self,
301
+ workspace_id: str,
302
+ task_id: str,
303
+ text: str,
304
+ *,
305
+ idempotency_key: str | None = None,
306
+ timeout: Timeout | _Default = DEFAULT,
307
+ ) -> CreateMessageResponse:
308
+ """``messages.create``: sends a follow-up on a task started from the API, CLI,
309
+ MCP or dashboard. The idempotency key works as in ``tasks.create``."""
310
+ key = idempotency_key if idempotency_key is not None else str(uuid.uuid4())
311
+ request = messages_create._get_kwargs(
312
+ workspace_id, task_id, body=CreateMessageRequest(text=text), idempotency_key=key
313
+ )
314
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, CreateMessageResponse.from_dict)
315
+
316
+ def list(
317
+ self,
318
+ workspace_id: str,
319
+ task_id: str,
320
+ *,
321
+ after: str | None = None,
322
+ limit: int | None = None,
323
+ timeout: Timeout | _Default = DEFAULT,
324
+ ) -> MessageList:
325
+ """``messages.list``: one page of the task's conversation, oldest first. To wait
326
+ for a reply, poll with ``after`` set to the ``id`` of the last entry you read."""
327
+ request = messages_list._get_kwargs(workspace_id, task_id, after=_unset(after), limit=_unset(limit))
328
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, MessageList.from_dict)
329
+
330
+ def list_all(
331
+ self,
332
+ workspace_id: str,
333
+ task_id: str,
334
+ *,
335
+ after: str | None = None,
336
+ limit: int | None = None,
337
+ timeout: Timeout | _Default = DEFAULT,
338
+ ) -> Iterator[Message]:
339
+ """Every entry after ``after`` (or from the start), following ``next`` page by page."""
340
+ while True:
341
+ page = self.list(workspace_id, task_id, after=after, limit=limit, timeout=timeout)
342
+ yield from page.messages
343
+ after = page.next_
344
+ if not after:
345
+ return
346
+
347
+
348
+ class TicketsApi:
349
+ """``tickets.*``: Linear and GitHub issues you can delegate."""
350
+
351
+ def __init__(self, transport: _Transport) -> None:
352
+ self._t = transport
353
+
354
+ def search(
355
+ self,
356
+ workspace_id: str,
357
+ *,
358
+ q: str | None = None,
359
+ repo: str | None = None,
360
+ timeout: Timeout | _Default = DEFAULT,
361
+ ) -> TicketSearchResult:
362
+ """``tickets.search``: open Linear and GitHub issues you could delegate."""
363
+ request = tickets_search._get_kwargs(workspace_id, q=_unset(q), repo=_unset(repo))
364
+ return self._t.decode(request, _retry.RETRY_IDEMPOTENT, timeout, TicketSearchResult.from_dict)
365
+
366
+ def delegate(
367
+ self,
368
+ workspace_id: str,
369
+ *,
370
+ source: Literal["linear", "github"],
371
+ id: str | None = None,
372
+ repo: str | None = None,
373
+ number: int | None = None,
374
+ timeout: Timeout | _Default = DEFAULT,
375
+ ) -> DelegateTicketResponse:
376
+ """``tickets.delegate``: hands Sinatra an existing issue as you: a Linear issue
377
+ by ``id`` (``source="linear"``), or a GitHub issue by ``repo`` and
378
+ ``number`` (``source="github"``). Never retried: the operation takes no
379
+ ``Idempotency-Key``."""
380
+ body: DelegateLinearTicket | DelegateGithubTicket
381
+ if source == "linear" and id is not None and repo is None and number is None:
382
+ body = DelegateLinearTicket(source="linear", id=id)
383
+ elif source == "github" and repo is not None and number is not None and id is None:
384
+ body = DelegateGithubTicket(source="github", repo=repo, number=number)
385
+ else:
386
+ raise ValueError(
387
+ 'Delegate a Linear issue with source="linear", id=..., or a GitHub issue with '
388
+ 'source="github", repo=..., number=...'
389
+ )
390
+ request = tickets_delegate._get_kwargs(workspace_id, body=body)
391
+ return self._t.decode(request, _retry.RETRY_NEVER, timeout, DelegateTicketResponse.from_dict)
sinatra_dev/_errors.py ADDED
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+
7
+
8
+ class SinatraError(Exception):
9
+ """The one error the client raises for an HTTP error response.
10
+
11
+ ``code`` is the API's machine-readable ``error`` code, such as
12
+ ``capacity_full``. New codes can appear at any time, so treat it as an open
13
+ string. Branch on ``code``, not on ``reason`` or the message.
14
+
15
+ Network failures and timeouts are not wrapped: they raise the
16
+ ``httpx.HTTPError`` that httpx raised.
17
+ """
18
+
19
+ status: int
20
+ """The HTTP status, such as ``409``."""
21
+ code: str
22
+ """The API's ``error`` code, such as ``not_running``. ``http_error`` when the
23
+ response had no code, for example an HTML page from a proxy."""
24
+ reason: str | None
25
+ """A sentence saying what went wrong, written to show to a person."""
26
+ request_id: str | None
27
+ """The request's id, from the body or the ``x-request-id`` header. Share it with support."""
28
+ body: Any
29
+ """The response body: the parsed JSON, or the text when it wasn't JSON."""
30
+ headers: httpx.Headers
31
+ """The response headers."""
32
+
33
+ def __init__(self, status: int, body: Any, headers: httpx.Headers) -> None:
34
+ fields: dict[str, Any] = body if isinstance(body, dict) else {}
35
+ error = fields.get("error")
36
+ reason = fields.get("reason")
37
+ request_id = fields.get("requestId")
38
+ self.status = status
39
+ self.code = error if isinstance(error, str) else "http_error"
40
+ self.reason = reason if isinstance(reason, str) else None
41
+ self.request_id = request_id if isinstance(request_id, str) else headers.get("x-request-id")
42
+ self.body = body
43
+ self.headers = headers
44
+ super().__init__(f"{status} {self.code}: {self.reason}" if self.reason else f"{status} {self.code}")
45
+
46
+ @classmethod
47
+ def from_response(cls, response: httpx.Response) -> SinatraError:
48
+ body: Any
49
+ try:
50
+ body = response.json()
51
+ except ValueError:
52
+ body = response.text
53
+ return cls(response.status_code, body, response.headers)
@@ -0,0 +1,8 @@
1
+ """A client library for accessing Sinatra API"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -0,0 +1 @@
1
+ """Contains methods for accessing the API"""
@@ -0,0 +1 @@
1
+ """Contains endpoint functions for accessing the API"""
@@ -0,0 +1,156 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import AuthenticatedClient, Client
8
+ from ...models.error import Error
9
+ from ...models.me import Me
10
+ from ...types import Response
11
+
12
+
13
+ def _get_kwargs() -> dict[str, Any]:
14
+
15
+ _kwargs: dict[str, Any] = {
16
+ "method": "get",
17
+ "url": "/v1/me",
18
+ }
19
+
20
+ return _kwargs
21
+
22
+
23
+ def _parse_response(
24
+ *, client: AuthenticatedClient | Client, response: httpx.Response
25
+ ) -> Error | Me | None:
26
+ if response.status_code == 200:
27
+ response_200 = Me.from_dict(response.json())
28
+
29
+ return response_200
30
+
31
+ if response.status_code == 401:
32
+ response_401 = Error.from_dict(response.json())
33
+
34
+ return response_401
35
+
36
+ if response.status_code == 429:
37
+ response_429 = Error.from_dict(response.json())
38
+
39
+ return response_429
40
+
41
+ if response.status_code == 500:
42
+ response_500 = Error.from_dict(response.json())
43
+
44
+ return response_500
45
+
46
+ if client.raise_on_unexpected_status:
47
+ raise errors.UnexpectedStatus(response.status_code, response.content)
48
+ else:
49
+ return None
50
+
51
+
52
+ def _build_response(
53
+ *, client: AuthenticatedClient | Client, response: httpx.Response
54
+ ) -> Response[Error | Me]:
55
+ return Response(
56
+ status_code=HTTPStatus(response.status_code),
57
+ content=response.content,
58
+ headers=response.headers,
59
+ parsed=_parse_response(client=client, response=response),
60
+ )
61
+
62
+
63
+ def sync_detailed(
64
+ *,
65
+ client: AuthenticatedClient | Client,
66
+ ) -> Response[Error | Me]:
67
+ """Get the token's user
68
+
69
+ Returns the user the token acts as and the workspaces they belong to, ordered by name. Use it to
70
+ find the workspace ids the other endpoints take.
71
+
72
+ Raises:
73
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
74
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
75
+
76
+ Returns:
77
+ Response[Error | Me]
78
+ """
79
+
80
+ kwargs = _get_kwargs()
81
+
82
+ response = client.get_httpx_client().request(
83
+ **kwargs,
84
+ )
85
+
86
+ return _build_response(client=client, response=response)
87
+
88
+
89
+ def sync(
90
+ *,
91
+ client: AuthenticatedClient | Client,
92
+ ) -> Error | Me | None:
93
+ """Get the token's user
94
+
95
+ Returns the user the token acts as and the workspaces they belong to, ordered by name. Use it to
96
+ find the workspace ids the other endpoints take.
97
+
98
+ Raises:
99
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
100
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
101
+
102
+ Returns:
103
+ Error | Me
104
+ """
105
+
106
+ return sync_detailed(
107
+ client=client,
108
+ ).parsed
109
+
110
+
111
+ async def asyncio_detailed(
112
+ *,
113
+ client: AuthenticatedClient | Client,
114
+ ) -> Response[Error | Me]:
115
+ """Get the token's user
116
+
117
+ Returns the user the token acts as and the workspaces they belong to, ordered by name. Use it to
118
+ find the workspace ids the other endpoints take.
119
+
120
+ Raises:
121
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
122
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
123
+
124
+ Returns:
125
+ Response[Error | Me]
126
+ """
127
+
128
+ kwargs = _get_kwargs()
129
+
130
+ response = await client.get_async_httpx_client().request(**kwargs)
131
+
132
+ return _build_response(client=client, response=response)
133
+
134
+
135
+ async def asyncio(
136
+ *,
137
+ client: AuthenticatedClient | Client,
138
+ ) -> Error | Me | None:
139
+ """Get the token's user
140
+
141
+ Returns the user the token acts as and the workspaces they belong to, ordered by name. Use it to
142
+ find the workspace ids the other endpoints take.
143
+
144
+ Raises:
145
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
146
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
147
+
148
+ Returns:
149
+ Error | Me
150
+ """
151
+
152
+ return (
153
+ await asyncio_detailed(
154
+ client=client,
155
+ )
156
+ ).parsed