cnovatech-sdk-core 0.1.0__tar.gz
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.
- cnovatech_sdk_core-0.1.0/.gitignore +26 -0
- cnovatech_sdk_core-0.1.0/CHANGELOG.md +57 -0
- cnovatech_sdk_core-0.1.0/LICENSE +21 -0
- cnovatech_sdk_core-0.1.0/PKG-INFO +53 -0
- cnovatech_sdk_core-0.1.0/README.md +23 -0
- cnovatech_sdk_core-0.1.0/pyproject.toml +86 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/__init__.py +24 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/client.py +146 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/config.py +257 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/dataframe.py +159 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/errors.py +450 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/metadata.py +23 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/models.py +95 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/operation.py +96 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/pagination.py +211 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/pricing.py +21 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/py.typed +0 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/resource.py +71 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/retry.py +119 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/transport.py +530 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/usage.py +110 -0
- cnovatech_sdk_core-0.1.0/src/cnovatech/sdk_core/validation.py +92 -0
- cnovatech_sdk_core-0.1.0/tests/conftest.py +162 -0
- cnovatech_sdk_core-0.1.0/tests/test_client.py +128 -0
- cnovatech_sdk_core-0.1.0/tests/test_config.py +227 -0
- cnovatech_sdk_core-0.1.0/tests/test_dataframe.py +212 -0
- cnovatech_sdk_core-0.1.0/tests/test_errors.py +308 -0
- cnovatech_sdk_core-0.1.0/tests/test_pagination.py +245 -0
- cnovatech_sdk_core-0.1.0/tests/test_pricing.py +23 -0
- cnovatech_sdk_core-0.1.0/tests/test_resource.py +117 -0
- cnovatech_sdk_core-0.1.0/tests/test_retry.py +307 -0
- cnovatech_sdk_core-0.1.0/tests/test_transport.py +621 -0
- cnovatech_sdk_core-0.1.0/tests/test_usage.py +179 -0
- cnovatech_sdk_core-0.1.0/tests/test_validation.py +120 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Environments and secrets
|
|
2
|
+
.venv/
|
|
3
|
+
.venv-*/
|
|
4
|
+
.env
|
|
5
|
+
.env.*
|
|
6
|
+
!.env.example
|
|
7
|
+
|
|
8
|
+
# Build output
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Tool caches
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
coverage.xml
|
|
19
|
+
htmlcov/
|
|
20
|
+
__pycache__/
|
|
21
|
+
*.pyc
|
|
22
|
+
|
|
23
|
+
# Editors and OS
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
.DS_Store
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `cnovatech-sdk-core` are documented here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
+
project uses [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
First release, together with `cnovatech-datamarket`. Rehearsed on TestPyPI as
|
|
12
|
+
0.1.0.dev1.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `ProductSettings` and `ClientConfig` with validation of API key (printable
|
|
17
|
+
ASCII only, so a key copied with an invisible character or a line break is
|
|
18
|
+
refused before any request), base URL (HTTPS required outside loopback),
|
|
19
|
+
timeouts (read timeout capped at the server's 300 s), retry count and
|
|
20
|
+
budget. Explicit arguments win over environment variables, which win over
|
|
21
|
+
product defaults.
|
|
22
|
+
- `Operation`: a transport-independent description of one API call, and
|
|
23
|
+
`Endpoint`: where a call lives, declared once per endpoint.
|
|
24
|
+
- `BaseSyncClient` / `BaseAsyncClient` and `SyncResource` / `AsyncResource`:
|
|
25
|
+
the client and resource plumbing every product SDK shares.
|
|
26
|
+
- `SyncTransport` and `AsyncTransport` over httpx, sharing request preparation
|
|
27
|
+
and response interpretation. Correlation ID per request, kept across
|
|
28
|
+
retries and reported on every error, including connection errors and
|
|
29
|
+
timeouts, even when the API does not echo it; descriptive `User-Agent`;
|
|
30
|
+
`None` query parameters omitted. Redirects are never followed and the
|
|
31
|
+
configured timeout applies per request, also through a user-supplied
|
|
32
|
+
`http_client`. Every `httpx.RequestError`, including a response body that
|
|
33
|
+
cannot be decoded, becomes an `APIConnectionError`.
|
|
34
|
+
- `RetryPolicy`: connection failures that happen before the request is sent,
|
|
35
|
+
502/503 and the per-minute 429 are repeated with capped exponential
|
|
36
|
+
backoff and jitter, honouring `Retry-After`. Read timeouts, 504 (the
|
|
37
|
+
application may still be processing the request), connections dropped
|
|
38
|
+
mid-response, quota errors and other 4xx/5xx are never repeated. Each
|
|
39
|
+
retry is logged at INFO with structured fields.
|
|
40
|
+
- `max_units_per_request`: an operation whose estimated cost is above the
|
|
41
|
+
ceiling raises `BudgetExceededError` before anything is sent.
|
|
42
|
+
- Exception hierarchy rooted at `CnovatechError`, with `APIStatusError`
|
|
43
|
+
subclasses chosen by HTTP status and, for 429, by error code.
|
|
44
|
+
`APIConnectionError.retryable` says whether a repeat could bill twice.
|
|
45
|
+
Every exception can be pickled, so it crosses process pools intact.
|
|
46
|
+
- `UsageTracker` and `RateLimitState`, fed by every response; a quota error
|
|
47
|
+
refreshes the last known balance without counting a call.
|
|
48
|
+
- `APIResponse` base model (immutable, keeps unknown fields) with
|
|
49
|
+
`request_info`, `Record` for nested objects, plus `Usage`, `PaginationMeta`
|
|
50
|
+
and `ListMeta`.
|
|
51
|
+
- `SyncPage` / `AsyncPage` with `iter_pages` and `iter_all` capped by
|
|
52
|
+
`max_pages` or `max_units`; pages can be pickled, without the ability to
|
|
53
|
+
fetch further pages. `TabularResponse.to_dataframe()` with nested rows,
|
|
54
|
+
envelope columns that collide with a row field prefixed by the response's
|
|
55
|
+
name, and pandas imported on first use.
|
|
56
|
+
- Validation helpers accept `uuid.UUID` identifiers and any iterable of
|
|
57
|
+
strings as a batch.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cnovatech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cnovatech-sdk-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared HTTP transport, configuration, errors and pagination for the Cnovatech Python SDKs.
|
|
5
|
+
Project-URL: Homepage, https://cnovatech.com.br
|
|
6
|
+
Project-URL: Repository, https://github.com/cnovatech-br/sdks
|
|
7
|
+
Project-URL: Changelog, https://github.com/cnovatech-br/sdks/blob/main/core/CHANGELOG.md
|
|
8
|
+
Author-email: Cnovatech <suporte@cnovatech.com.br>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: client,cnovatech,http,sdk
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
26
|
+
Requires-Dist: pydantic<3.0,>=2.12
|
|
27
|
+
Provides-Extra: pandas
|
|
28
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# cnovatech-sdk-core
|
|
32
|
+
|
|
33
|
+
Shared foundation of the Cnovatech Python SDKs: configuration, HTTP transport
|
|
34
|
+
(sync and async), error hierarchy, pagination and quota accounting.
|
|
35
|
+
|
|
36
|
+
This package is not meant to be installed on its own. Product SDKs such as
|
|
37
|
+
[`cnovatech-datamarket`](https://pypi.org/project/cnovatech-datamarket/)
|
|
38
|
+
depend on it and re-export everything their users need. If you are
|
|
39
|
+
integrating with a Cnovatech API, install the product SDK. Source and issues:
|
|
40
|
+
<https://github.com/cnovatech-br/sdks>.
|
|
41
|
+
|
|
42
|
+
## Boundary
|
|
43
|
+
|
|
44
|
+
The core knows nothing about any product: no endpoint, no field name, no domain
|
|
45
|
+
concept. CI fails if a product term appears under `core/src`. Product-specific
|
|
46
|
+
behaviour reaches the core only through `ProductSettings` (environment prefix,
|
|
47
|
+
base URL, API prefix, error overrides) and `Operation` objects built by the
|
|
48
|
+
product's resources.
|
|
49
|
+
|
|
50
|
+
## Versioning
|
|
51
|
+
|
|
52
|
+
The core and the product SDKs are released together and share a version number.
|
|
53
|
+
A product SDK pins the core to the same minor series.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# cnovatech-sdk-core
|
|
2
|
+
|
|
3
|
+
Shared foundation of the Cnovatech Python SDKs: configuration, HTTP transport
|
|
4
|
+
(sync and async), error hierarchy, pagination and quota accounting.
|
|
5
|
+
|
|
6
|
+
This package is not meant to be installed on its own. Product SDKs such as
|
|
7
|
+
[`cnovatech-datamarket`](https://pypi.org/project/cnovatech-datamarket/)
|
|
8
|
+
depend on it and re-export everything their users need. If you are
|
|
9
|
+
integrating with a Cnovatech API, install the product SDK. Source and issues:
|
|
10
|
+
<https://github.com/cnovatech-br/sdks>.
|
|
11
|
+
|
|
12
|
+
## Boundary
|
|
13
|
+
|
|
14
|
+
The core knows nothing about any product: no endpoint, no field name, no domain
|
|
15
|
+
concept. CI fails if a product term appears under `core/src`. Product-specific
|
|
16
|
+
behaviour reaches the core only through `ProductSettings` (environment prefix,
|
|
17
|
+
base URL, API prefix, error overrides) and `Operation` objects built by the
|
|
18
|
+
product's resources.
|
|
19
|
+
|
|
20
|
+
## Versioning
|
|
21
|
+
|
|
22
|
+
The core and the product SDKs are released together and share a version number.
|
|
23
|
+
A product SDK pins the core to the same minor series.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cnovatech-sdk-core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Shared HTTP transport, configuration, errors and pagination for the Cnovatech Python SDKs."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Cnovatech", email = "suporte@cnovatech.com.br" }]
|
|
10
|
+
keywords = ["cnovatech", "sdk", "http", "client"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
# pydantic 2.12 is the first release with wheels for Python 3.14.
|
|
26
|
+
dependencies = [
|
|
27
|
+
"httpx>=0.27,<1.0",
|
|
28
|
+
"pydantic>=2.12,<3.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
pandas = ["pandas>=2.0"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://cnovatech.com.br"
|
|
36
|
+
Repository = "https://github.com/cnovatech-br/sdks"
|
|
37
|
+
Changelog = "https://github.com/cnovatech-br/sdks/blob/main/core/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["hatchling>=1.27,<2"]
|
|
41
|
+
build-backend = "hatchling.build"
|
|
42
|
+
|
|
43
|
+
# `src/cnovatech/` has no __init__.py on purpose: it is a PEP 420 namespace
|
|
44
|
+
# shared with the product SDKs (`cnovatech.datamarket`, ...).
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/cnovatech"]
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.sdist]
|
|
49
|
+
include = ["src", "tests", "README.md", "CHANGELOG.md", "LICENSE", "pyproject.toml"]
|
|
50
|
+
|
|
51
|
+
[tool.mypy]
|
|
52
|
+
python_version = "3.10"
|
|
53
|
+
files = ["src", "tests"]
|
|
54
|
+
mypy_path = "src:tests"
|
|
55
|
+
strict = true
|
|
56
|
+
warn_unreachable = true
|
|
57
|
+
namespace_packages = true
|
|
58
|
+
explicit_package_bases = true
|
|
59
|
+
plugins = ["pydantic.mypy"]
|
|
60
|
+
|
|
61
|
+
[tool.pydantic-mypy]
|
|
62
|
+
init_forbid_extra = true
|
|
63
|
+
init_typed = true
|
|
64
|
+
warn_required_dynamic_aliases = true
|
|
65
|
+
|
|
66
|
+
[tool.pytest.ini_options]
|
|
67
|
+
testpaths = ["tests"]
|
|
68
|
+
asyncio_mode = "auto"
|
|
69
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
70
|
+
addopts = "-q --strict-markers --strict-config"
|
|
71
|
+
filterwarnings = ["error"]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
source = ["src/cnovatech/sdk_core"]
|
|
75
|
+
branch = true
|
|
76
|
+
|
|
77
|
+
[tool.coverage.report]
|
|
78
|
+
fail_under = 90
|
|
79
|
+
show_missing = true
|
|
80
|
+
exclude_also = [
|
|
81
|
+
"if TYPE_CHECKING:",
|
|
82
|
+
"if sys.version_info",
|
|
83
|
+
"@overload",
|
|
84
|
+
"raise NotImplementedError",
|
|
85
|
+
"\\.\\.\\.$",
|
|
86
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Shared foundation of the Cnovatech Python SDKs.
|
|
2
|
+
|
|
3
|
+
This package is not used directly by application code. The product SDKs build
|
|
4
|
+
on its modules (``config``, ``transport``, ``client``, ``resource``, ``models``,
|
|
5
|
+
``pagination``, ``errors``, ...) and re-export what their users need. It knows
|
|
6
|
+
nothing about any product: no endpoint, field name or domain concept lives
|
|
7
|
+
here, and CI enforces that boundary.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
from .config import CORE_DISTRIBUTION_NAME
|
|
15
|
+
from .metadata import read_distribution_version
|
|
16
|
+
|
|
17
|
+
__version__ = read_distribution_version(CORE_DISTRIBUTION_NAME)
|
|
18
|
+
|
|
19
|
+
# Libraries log but never configure logging. The null handler keeps Python from
|
|
20
|
+
# printing "No handlers could be found" when the application has not set up
|
|
21
|
+
# logging; the application routes the `cnovatech` logger wherever it wants.
|
|
22
|
+
logging.getLogger("cnovatech").addHandler(logging.NullHandler())
|
|
23
|
+
|
|
24
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""What every product client shares: configuration, transport, accounting and lifecycle.
|
|
2
|
+
|
|
3
|
+
A product SDK subclasses :class:`BaseSyncClient` and :class:`BaseAsyncClient`,
|
|
4
|
+
binds its :class:`ProductSettings` and attaches its resources. Everything
|
|
5
|
+
else, from reading the environment to closing the connection pool, is here.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from .config import DEFAULT_MAX_RETRIES, DEFAULT_READ_TIMEOUT_SECONDS, build_client_config
|
|
13
|
+
from .transport import AsyncTransport, SyncTransport
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from types import TracebackType
|
|
17
|
+
|
|
18
|
+
import httpx
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
21
|
+
from .config import ProductSettings
|
|
22
|
+
from .usage import RateLimitState, UsageTracker
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BaseSyncClient:
|
|
26
|
+
"""A client using blocking I/O. See the product client for the constructor arguments."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
product: ProductSettings,
|
|
31
|
+
api_key: str | None = None,
|
|
32
|
+
*,
|
|
33
|
+
base_url: str | None = None,
|
|
34
|
+
timeout: float | httpx.Timeout = DEFAULT_READ_TIMEOUT_SECONDS,
|
|
35
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
36
|
+
max_units_per_request: int | None = None,
|
|
37
|
+
http_client: httpx.Client | None = None,
|
|
38
|
+
transport: httpx.BaseTransport | None = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._config = build_client_config(
|
|
41
|
+
product,
|
|
42
|
+
api_key=api_key,
|
|
43
|
+
base_url=base_url,
|
|
44
|
+
timeout=timeout,
|
|
45
|
+
max_retries=max_retries,
|
|
46
|
+
max_units_per_request=max_units_per_request,
|
|
47
|
+
)
|
|
48
|
+
self._transport = SyncTransport(self._config, http_client=http_client, transport=transport)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def base_url(self) -> str:
|
|
52
|
+
"""Origin the client talks to."""
|
|
53
|
+
return self._config.base_url
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def usage(self) -> UsageTracker:
|
|
57
|
+
"""Quota spent through this client: ``spent``, ``calls`` and the ``last`` usage block."""
|
|
58
|
+
return self._transport.usage
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def rate_limit(self) -> RateLimitState:
|
|
62
|
+
"""The per-minute rate limit as last reported by the API."""
|
|
63
|
+
return self._transport.rate_limit
|
|
64
|
+
|
|
65
|
+
def close(self) -> None:
|
|
66
|
+
"""Release the connection pool. Clients supplied through ``http_client`` are left open."""
|
|
67
|
+
self._transport.close()
|
|
68
|
+
|
|
69
|
+
def __enter__(self) -> Self:
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
def __exit__(
|
|
73
|
+
self,
|
|
74
|
+
exc_type: type[BaseException] | None,
|
|
75
|
+
exc: BaseException | None,
|
|
76
|
+
traceback: TracebackType | None,
|
|
77
|
+
) -> None:
|
|
78
|
+
self.close()
|
|
79
|
+
|
|
80
|
+
def __repr__(self) -> str:
|
|
81
|
+
return (
|
|
82
|
+
f"{type(self).__name__}(base_url={self._config.base_url!r}, "
|
|
83
|
+
f"api_key={self._config.masked_api_key!r})"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class BaseAsyncClient:
|
|
88
|
+
"""A client using asyncio. See the product client for the constructor arguments."""
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
product: ProductSettings,
|
|
93
|
+
api_key: str | None = None,
|
|
94
|
+
*,
|
|
95
|
+
base_url: str | None = None,
|
|
96
|
+
timeout: float | httpx.Timeout = DEFAULT_READ_TIMEOUT_SECONDS,
|
|
97
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
98
|
+
max_units_per_request: int | None = None,
|
|
99
|
+
http_client: httpx.AsyncClient | None = None,
|
|
100
|
+
transport: httpx.AsyncBaseTransport | None = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
self._config = build_client_config(
|
|
103
|
+
product,
|
|
104
|
+
api_key=api_key,
|
|
105
|
+
base_url=base_url,
|
|
106
|
+
timeout=timeout,
|
|
107
|
+
max_retries=max_retries,
|
|
108
|
+
max_units_per_request=max_units_per_request,
|
|
109
|
+
)
|
|
110
|
+
self._transport = AsyncTransport(self._config, http_client=http_client, transport=transport)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def base_url(self) -> str:
|
|
114
|
+
"""Origin the client talks to."""
|
|
115
|
+
return self._config.base_url
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def usage(self) -> UsageTracker:
|
|
119
|
+
"""Quota spent through this client: ``spent``, ``calls`` and the ``last`` usage block."""
|
|
120
|
+
return self._transport.usage
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def rate_limit(self) -> RateLimitState:
|
|
124
|
+
"""The per-minute rate limit as last reported by the API."""
|
|
125
|
+
return self._transport.rate_limit
|
|
126
|
+
|
|
127
|
+
async def aclose(self) -> None:
|
|
128
|
+
"""Release the connection pool. Clients supplied through ``http_client`` are left open."""
|
|
129
|
+
await self._transport.aclose()
|
|
130
|
+
|
|
131
|
+
async def __aenter__(self) -> Self:
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
async def __aexit__(
|
|
135
|
+
self,
|
|
136
|
+
exc_type: type[BaseException] | None,
|
|
137
|
+
exc: BaseException | None,
|
|
138
|
+
traceback: TracebackType | None,
|
|
139
|
+
) -> None:
|
|
140
|
+
await self.aclose()
|
|
141
|
+
|
|
142
|
+
def __repr__(self) -> str:
|
|
143
|
+
return (
|
|
144
|
+
f"{type(self).__name__}(base_url={self._config.base_url!r}, "
|
|
145
|
+
f"api_key={self._config.masked_api_key!r})"
|
|
146
|
+
)
|