pyquickbase 0.1.0a1__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.
- pyquickbase/__init__.py +73 -0
- pyquickbase/_async/__init__.py +0 -0
- pyquickbase/_async/_client.py +151 -0
- pyquickbase/_async/_resources.py +511 -0
- pyquickbase/_async/_transport.py +74 -0
- pyquickbase/_auth.py +19 -0
- pyquickbase/_config.py +93 -0
- pyquickbase/_models.py +241 -0
- pyquickbase/_query.py +315 -0
- pyquickbase/_response.py +90 -0
- pyquickbase/_retry.py +66 -0
- pyquickbase/_schema.py +35 -0
- pyquickbase/_sync/__init__.py +0 -0
- pyquickbase/_sync/_client.py +152 -0
- pyquickbase/_sync/_resources.py +506 -0
- pyquickbase/_sync/_transport.py +75 -0
- pyquickbase/_version.py +1 -0
- pyquickbase/_writes.py +104 -0
- pyquickbase/errors.py +110 -0
- pyquickbase/py.typed +0 -0
- pyquickbase-0.1.0a1.dist-info/METADATA +223 -0
- pyquickbase-0.1.0a1.dist-info/RECORD +24 -0
- pyquickbase-0.1.0a1.dist-info/WHEEL +4 -0
- pyquickbase-0.1.0a1.dist-info/licenses/LICENSE +21 -0
pyquickbase/__init__.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""pyquickbase: an unofficial, typed Python SDK for the Quickbase API."""
|
|
2
|
+
|
|
3
|
+
from ._async._client import AsyncQuickbase
|
|
4
|
+
from ._async._resources import AsyncApp, AsyncRecordIterator, AsyncTable
|
|
5
|
+
from ._models import AppInfo, Field, Page, Record, Relationship, TableInfo
|
|
6
|
+
from ._query import F, WhereCondition, parse_where
|
|
7
|
+
from ._retry import RetryPolicy
|
|
8
|
+
from ._sync._client import Quickbase
|
|
9
|
+
from ._sync._resources import App, RecordIterator, Table
|
|
10
|
+
from ._version import __version__
|
|
11
|
+
from ._writes import RowError, WriteResult
|
|
12
|
+
from .errors import (
|
|
13
|
+
APIError,
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
BadRequestError,
|
|
16
|
+
ConfigurationError,
|
|
17
|
+
InvalidResponseError,
|
|
18
|
+
InvalidValueError,
|
|
19
|
+
NetworkError,
|
|
20
|
+
NotFoundError,
|
|
21
|
+
PermissionDeniedError,
|
|
22
|
+
QueryError,
|
|
23
|
+
QuickbaseError,
|
|
24
|
+
RateLimitError,
|
|
25
|
+
ReadOnlyError,
|
|
26
|
+
RequestTimeoutError,
|
|
27
|
+
ServerError,
|
|
28
|
+
UnknownFieldError,
|
|
29
|
+
UnknownTableError,
|
|
30
|
+
WriteError,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"APIError",
|
|
35
|
+
"App",
|
|
36
|
+
"AppInfo",
|
|
37
|
+
"AsyncApp",
|
|
38
|
+
"AsyncQuickbase",
|
|
39
|
+
"AsyncRecordIterator",
|
|
40
|
+
"AsyncTable",
|
|
41
|
+
"AuthenticationError",
|
|
42
|
+
"BadRequestError",
|
|
43
|
+
"ConfigurationError",
|
|
44
|
+
"F",
|
|
45
|
+
"Field",
|
|
46
|
+
"InvalidResponseError",
|
|
47
|
+
"InvalidValueError",
|
|
48
|
+
"NetworkError",
|
|
49
|
+
"NotFoundError",
|
|
50
|
+
"Page",
|
|
51
|
+
"PermissionDeniedError",
|
|
52
|
+
"QueryError",
|
|
53
|
+
"Quickbase",
|
|
54
|
+
"QuickbaseError",
|
|
55
|
+
"RateLimitError",
|
|
56
|
+
"ReadOnlyError",
|
|
57
|
+
"Record",
|
|
58
|
+
"RecordIterator",
|
|
59
|
+
"Relationship",
|
|
60
|
+
"RequestTimeoutError",
|
|
61
|
+
"RetryPolicy",
|
|
62
|
+
"RowError",
|
|
63
|
+
"ServerError",
|
|
64
|
+
"Table",
|
|
65
|
+
"TableInfo",
|
|
66
|
+
"UnknownFieldError",
|
|
67
|
+
"UnknownTableError",
|
|
68
|
+
"WhereCondition",
|
|
69
|
+
"WriteError",
|
|
70
|
+
"WriteResult",
|
|
71
|
+
"__version__",
|
|
72
|
+
"parse_where",
|
|
73
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""The async Quickbase client."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Collection, Mapping
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import Any, Self
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from .._auth import UserTokenAuth
|
|
11
|
+
from .._config import (
|
|
12
|
+
DEFAULT_BASE_URL,
|
|
13
|
+
DEFAULT_TIMEOUT,
|
|
14
|
+
build_user_agent,
|
|
15
|
+
normalize_allowed_writes,
|
|
16
|
+
normalize_realm,
|
|
17
|
+
read_env,
|
|
18
|
+
validate_base_url,
|
|
19
|
+
validate_token,
|
|
20
|
+
)
|
|
21
|
+
from .._retry import RetryPolicy
|
|
22
|
+
from .._schema import SchemaCache
|
|
23
|
+
from ..errors import ConfigurationError
|
|
24
|
+
from ._resources import AsyncApp
|
|
25
|
+
from ._transport import AsyncTransport
|
|
26
|
+
|
|
27
|
+
DEFAULT_SCHEMA_TTL = 300.0
|
|
28
|
+
_APP_ID = re.compile(r"[A-Za-z0-9]+")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AsyncQuickbase:
|
|
32
|
+
"""A Quickbase API client. Use it as a context manager so connections get closed.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
realm: Your realm, as `acme`, `acme.quickbase.com`, or a realm URL.
|
|
36
|
+
token: A Quickbase user token.
|
|
37
|
+
base_url: The API root. Must use HTTPS, except for loopback addresses.
|
|
38
|
+
retry: How to retry rate limits and connection failures.
|
|
39
|
+
timeout: Request timeout in seconds. Ignored if you pass `http_client`.
|
|
40
|
+
user_agent: Identifies your application, placed before the SDK's own User-Agent.
|
|
41
|
+
http_client: Your own configured httpx client, for proxies, custom TLS, event hooks,
|
|
42
|
+
or custom transports. You remain responsible for closing it.
|
|
43
|
+
schema_ttl: Seconds to cache app, table, and field metadata. Use 0 to disable caching.
|
|
44
|
+
read_only: Refuse any request that could change data, raising `ReadOnlyError`. Only GET
|
|
45
|
+
requests and record queries are sent, including through `request()`.
|
|
46
|
+
allowed_writes: Like `read_only`, but also allow these `(method, path)` pairs, such as
|
|
47
|
+
`{("POST", "/records")}`. Paths must match exactly.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
realm: str,
|
|
54
|
+
token: str,
|
|
55
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
56
|
+
retry: RetryPolicy | None = None,
|
|
57
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
58
|
+
user_agent: str | None = None,
|
|
59
|
+
http_client: httpx.AsyncClient | None = None,
|
|
60
|
+
schema_ttl: float = DEFAULT_SCHEMA_TTL,
|
|
61
|
+
read_only: bool = False,
|
|
62
|
+
allowed_writes: Collection[tuple[str, str]] | None = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
self._realm = normalize_realm(realm)
|
|
65
|
+
self._transport = AsyncTransport(
|
|
66
|
+
realm=self._realm,
|
|
67
|
+
auth=UserTokenAuth(validate_token(token)),
|
|
68
|
+
base_url=validate_base_url(base_url),
|
|
69
|
+
retry=RetryPolicy() if retry is None else retry,
|
|
70
|
+
timeout=timeout,
|
|
71
|
+
user_agent=build_user_agent(user_agent),
|
|
72
|
+
http_client=http_client,
|
|
73
|
+
allowed_writes=normalize_allowed_writes(read_only, allowed_writes),
|
|
74
|
+
)
|
|
75
|
+
self._schema = SchemaCache(schema_ttl)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_env(
|
|
79
|
+
cls,
|
|
80
|
+
*,
|
|
81
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
82
|
+
retry: RetryPolicy | None = None,
|
|
83
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
84
|
+
user_agent: str | None = None,
|
|
85
|
+
http_client: httpx.AsyncClient | None = None,
|
|
86
|
+
schema_ttl: float = DEFAULT_SCHEMA_TTL,
|
|
87
|
+
read_only: bool = False,
|
|
88
|
+
allowed_writes: Collection[tuple[str, str]] | None = None,
|
|
89
|
+
) -> Self:
|
|
90
|
+
"""Create a client from the `QB_REALM` and `QB_USER_TOKEN` environment variables."""
|
|
91
|
+
realm, token = read_env()
|
|
92
|
+
return cls(
|
|
93
|
+
realm=realm,
|
|
94
|
+
token=token,
|
|
95
|
+
base_url=base_url,
|
|
96
|
+
retry=retry,
|
|
97
|
+
timeout=timeout,
|
|
98
|
+
user_agent=user_agent,
|
|
99
|
+
http_client=http_client,
|
|
100
|
+
schema_ttl=schema_ttl,
|
|
101
|
+
read_only=read_only,
|
|
102
|
+
allowed_writes=allowed_writes,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def realm(self) -> str:
|
|
107
|
+
"""The full realm hostname, such as `acme.quickbase.com`."""
|
|
108
|
+
return self._realm
|
|
109
|
+
|
|
110
|
+
def app(self, app_id: str) -> AsyncApp:
|
|
111
|
+
"""Get an app by ID, the `bq...` part of its URL. This makes no network calls."""
|
|
112
|
+
if not _APP_ID.fullmatch(app_id):
|
|
113
|
+
raise ConfigurationError("An app ID contains only letters and numbers.")
|
|
114
|
+
return AsyncApp(self._transport, self._schema, app_id)
|
|
115
|
+
|
|
116
|
+
def clear_schema_cache(self) -> None:
|
|
117
|
+
"""Forget cached metadata, for example after changing a table's fields."""
|
|
118
|
+
self._schema.clear()
|
|
119
|
+
|
|
120
|
+
async def request(
|
|
121
|
+
self,
|
|
122
|
+
method: str,
|
|
123
|
+
path: str,
|
|
124
|
+
*,
|
|
125
|
+
params: Mapping[str, Any] | None = None,
|
|
126
|
+
json: Any = None,
|
|
127
|
+
) -> Any:
|
|
128
|
+
"""Call any Quickbase endpoint directly and return its decoded JSON body.
|
|
129
|
+
|
|
130
|
+
This is the escape hatch for endpoints without a dedicated method. `path` is relative
|
|
131
|
+
to the API root, such as `/tables/abc123`, and the body is None if the response is empty.
|
|
132
|
+
"""
|
|
133
|
+
return await self._transport.request(method, path, params=params, json=json)
|
|
134
|
+
|
|
135
|
+
async def aclose(self) -> None:
|
|
136
|
+
"""Close connections. Safe to call more than once."""
|
|
137
|
+
await self._transport.aclose()
|
|
138
|
+
|
|
139
|
+
async def __aenter__(self) -> Self:
|
|
140
|
+
return self
|
|
141
|
+
|
|
142
|
+
async def __aexit__(
|
|
143
|
+
self,
|
|
144
|
+
exc_type: type[BaseException] | None,
|
|
145
|
+
exc: BaseException | None,
|
|
146
|
+
traceback: TracebackType | None,
|
|
147
|
+
) -> None:
|
|
148
|
+
await self.aclose()
|
|
149
|
+
|
|
150
|
+
def __repr__(self) -> str:
|
|
151
|
+
return f"{type(self).__name__}(realm={self._realm!r})"
|