arcobi-sdk 0.1.0b1__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.
- arcobi_sdk-0.1.0b1/.gitignore +12 -0
- arcobi_sdk-0.1.0b1/LICENSE +37 -0
- arcobi_sdk-0.1.0b1/PKG-INFO +164 -0
- arcobi_sdk-0.1.0b1/README.md +123 -0
- arcobi_sdk-0.1.0b1/deploy/pool-server/Dockerfile +30 -0
- arcobi_sdk-0.1.0b1/deploy/pool-server/README.md +108 -0
- arcobi_sdk-0.1.0b1/deploy/pool-server/credentials_pool.example.yaml +23 -0
- arcobi_sdk-0.1.0b1/deploy/pool-server/docker-compose.yml +53 -0
- arcobi_sdk-0.1.0b1/docs/CREDENTIALS.md +98 -0
- arcobi_sdk-0.1.0b1/docs/EXCEL.md +154 -0
- arcobi_sdk-0.1.0b1/pyproject.toml +107 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/__init__.py +88 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/cli.py +395 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/config.py +78 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/datahub/__init__.py +63 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/datahub/client.py +525 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/datahub/extract.py +588 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/datahub/specs.py +247 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/errors.py +68 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/excel/__init__.py +58 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/excel/convert.py +361 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/excel/udf.py +369 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/excel/vba/ArcobiSdk.bas +327 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/excel/vba/README.md +157 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/io.py +234 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/__init__.py +35 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/client.py +704 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/priority.py +105 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/protocol.py +143 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/__init__.py +12 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/__main__.py +55 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/api/__init__.py +1 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/api/deps.py +55 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/api/routes.py +113 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/api/websocket.py +141 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/app.py +111 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/config.py +88 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/__init__.py +1 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/credentials.py +146 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/lease_manager.py +639 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/metrics.py +85 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/queue.py +179 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/core/store.py +973 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/providers/__init__.py +80 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/providers/base.py +68 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/providers/datahub.py +182 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/pool/server/providers/static.py +57 -0
- arcobi_sdk-0.1.0b1/src/arcobi_sdk/timeutils.py +76 -0
- arcobi_sdk-0.1.0b1/tests/__init__.py +0 -0
- arcobi_sdk-0.1.0b1/tests/datahub/__init__.py +0 -0
- arcobi_sdk-0.1.0b1/tests/datahub/test_client.py +383 -0
- arcobi_sdk-0.1.0b1/tests/datahub/test_extract.py +469 -0
- arcobi_sdk-0.1.0b1/tests/datahub/test_specs.py +126 -0
- arcobi_sdk-0.1.0b1/tests/excel/__init__.py +0 -0
- arcobi_sdk-0.1.0b1/tests/excel/test_convert.py +239 -0
- arcobi_sdk-0.1.0b1/tests/excel/test_udf.py +308 -0
- arcobi_sdk-0.1.0b1/tests/live/__init__.py +0 -0
- arcobi_sdk-0.1.0b1/tests/live/conftest.py +43 -0
- arcobi_sdk-0.1.0b1/tests/live/test_datahub_concurrency_live.py +72 -0
- arcobi_sdk-0.1.0b1/tests/live/test_datahub_live.py +56 -0
- arcobi_sdk-0.1.0b1/tests/pool/__init__.py +1 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/__init__.py +1 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/conftest.py +87 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_api.py +159 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_lease_manager.py +214 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_main.py +79 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_postgres_mappers.py +51 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_postgres_store.py +383 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_scheduling.py +77 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_units.py +309 -0
- arcobi_sdk-0.1.0b1/tests/pool/server/test_websocket.py +286 -0
- arcobi_sdk-0.1.0b1/tests/pool/test_client.py +496 -0
- arcobi_sdk-0.1.0b1/tests/test_cli.py +298 -0
- arcobi_sdk-0.1.0b1/tests/test_config.py +51 -0
- arcobi_sdk-0.1.0b1/tests/test_io.py +131 -0
- arcobi_sdk-0.1.0b1/tests/test_priority.py +60 -0
- arcobi_sdk-0.1.0b1/tests/test_protocol.py +52 -0
- arcobi_sdk-0.1.0b1/tests/test_timeutils.py +56 -0
- arcobi_sdk-0.1.0b1/tests/test_version.py +13 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Arcobi SDK Licence
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arcobi. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and its accompanying documentation (the "Software") are the
|
|
6
|
+
proprietary property of Arcobi. The Software is licensed, not sold.
|
|
7
|
+
|
|
8
|
+
1. Grant. Subject to a current written agreement with Arcobi covering access to
|
|
9
|
+
the Arcobi data API, Arcobi grants you a non-exclusive, non-transferable,
|
|
10
|
+
revocable licence to install and use the Software, in unmodified form, solely
|
|
11
|
+
to access that API for your own internal business purposes.
|
|
12
|
+
|
|
13
|
+
2. Restrictions. Except as that agreement or applicable mandatory law permits,
|
|
14
|
+
you may not: copy the Software other than as reasonably required for backup
|
|
15
|
+
or internal deployment; modify it or create derivative works from it;
|
|
16
|
+
distribute, sublicense, sell, rent, lease or otherwise make it available to
|
|
17
|
+
any third party; remove or alter any proprietary notice in it; or reverse
|
|
18
|
+
engineer, decompile or disassemble it.
|
|
19
|
+
|
|
20
|
+
3. Reservation of rights. Arcobi retains all right, title and interest in and to
|
|
21
|
+
the Software. No rights are granted other than those expressly stated here.
|
|
22
|
+
|
|
23
|
+
4. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
24
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
25
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
26
|
+
|
|
27
|
+
5. Limitation of liability. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW,
|
|
28
|
+
ARCOBI SHALL NOT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
29
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
30
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN THE SOFTWARE.
|
|
31
|
+
|
|
32
|
+
6. Termination. This licence terminates automatically if you breach it, or when
|
|
33
|
+
your agreement with Arcobi ends. On termination you must stop using the
|
|
34
|
+
Software and delete all copies in your possession.
|
|
35
|
+
|
|
36
|
+
7. Third-party components. The Software depends on separately distributed
|
|
37
|
+
third-party packages, which remain governed by their own licences.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arcobi-sdk
|
|
3
|
+
Version: 0.1.0b1
|
|
4
|
+
Summary: Fetch, align and export energy-market time-series data (Datahub), with an optional shared credential pool.
|
|
5
|
+
Project-URL: Documentation, https://sdk.arcobi.com
|
|
6
|
+
Author: Arcobi
|
|
7
|
+
License-Expression: LicenseRef-Proprietary
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: aeso,data,datahub,energy,ercot,ieso,time-series
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: httpx>=0.25
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
20
|
+
Requires-Dist: pandas<4,>=2.2
|
|
21
|
+
Requires-Dist: pydantic>=2.0
|
|
22
|
+
Requires-Dist: websockets>=12
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
25
|
+
Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'all'
|
|
26
|
+
Requires-Dist: pyarrow>=14; extra == 'all'
|
|
27
|
+
Requires-Dist: pydantic-settings>=2.0; extra == 'all'
|
|
28
|
+
Requires-Dist: pyyaml>=6.0; extra == 'all'
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == 'all'
|
|
30
|
+
Provides-Extra: excel
|
|
31
|
+
Requires-Dist: xlwings>=0.30; extra == 'excel'
|
|
32
|
+
Provides-Extra: parquet
|
|
33
|
+
Requires-Dist: pyarrow>=14; extra == 'parquet'
|
|
34
|
+
Provides-Extra: pool-server
|
|
35
|
+
Requires-Dist: fastapi>=0.110; extra == 'pool-server'
|
|
36
|
+
Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'pool-server'
|
|
37
|
+
Requires-Dist: pydantic-settings>=2.0; extra == 'pool-server'
|
|
38
|
+
Requires-Dist: pyyaml>=6.0; extra == 'pool-server'
|
|
39
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == 'pool-server'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# arcobi-sdk
|
|
43
|
+
|
|
44
|
+
Load energy-market time-series data from Datahub, align it on a time grid, and export it to a
|
|
45
|
+
DataFrame, CSV, Parquet or JSON. Optionally share a pool of vendor credentials across many jobs
|
|
46
|
+
through a small lease service. It takes you from the API to a clean, aligned frame or file,
|
|
47
|
+
ready for the features and models you build on top.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install arcobi-sdk # extraction + export + pool client
|
|
51
|
+
pip install "arcobi-sdk[parquet]" # + Parquet export
|
|
52
|
+
pip install "arcobi-sdk[pool-server]" # + the credential-pool service
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from datetime import datetime, timedelta
|
|
59
|
+
|
|
60
|
+
from arcobi_sdk import DatahubClient, fetch_streams, to_csv
|
|
61
|
+
|
|
62
|
+
end = datetime.now()
|
|
63
|
+
start = end - timedelta(days=7)
|
|
64
|
+
|
|
65
|
+
with DatahubClient(username="...", password="...") as client:
|
|
66
|
+
df = fetch_streams(
|
|
67
|
+
client, streams=[1001, 1002], start=start, end=end, names=["demand", "price"]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
to_csv(df, "week.csv")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`fetch_streams` fetches the streams one after another. To fetch in parallel, share the same
|
|
74
|
+
client across threads: it is thread-safe and holds the account's single token. Never open a
|
|
75
|
+
second client with the same account, Datahub allows one session per account (see the
|
|
76
|
+
[credentials guide](https://sdk.arcobi.com/credentials)).
|
|
77
|
+
|
|
78
|
+
Credentials can also come from the environment (`DATAHUB_USERNAME` / `DATAHUB_PASSWORD`,
|
|
79
|
+
or a pre-issued `DATAHUB_BEARER_TOKEN`), or from a local `.env` file loaded with
|
|
80
|
+
`arcobi_sdk.load_env()`. The [credentials guide](https://sdk.arcobi.com/credentials) covers every
|
|
81
|
+
option and the live smoke test.
|
|
82
|
+
|
|
83
|
+
## Sharing credentials across jobs
|
|
84
|
+
|
|
85
|
+
If several processes share a handful of vendor accounts, run the pool service once and let each
|
|
86
|
+
job lease a token. Leases carry a generic **priority level**: `0` is served first, then `1`, then
|
|
87
|
+
`2`; the default names are `high`, `normal`, `low` and deployments may rename them.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from arcobi_sdk import DatahubClient, fetch_streams
|
|
91
|
+
from arcobi_sdk.pool import PoolClient
|
|
92
|
+
|
|
93
|
+
pool = PoolClient("http://localhost:8002", api_key="...")
|
|
94
|
+
with pool.lease(holder_id="nightly-report", priority="high") as lease:
|
|
95
|
+
with DatahubClient(token_provider=lease.token_provider) as client:
|
|
96
|
+
df = fetch_streams(client, [1001], start, end)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The token is refreshed by the service before it expires and the client picks the new one up
|
|
100
|
+
mid-extraction. Running the service is described in the
|
|
101
|
+
[credential-pool guide](https://sdk.arcobi.com/examples/credential-pool).
|
|
102
|
+
|
|
103
|
+
## Command line and Excel
|
|
104
|
+
|
|
105
|
+
Every install also provides a small CLI, handy for scripts, schedulers and spreadsheet macros:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
arcobi-sdk check # authenticate, print token expiry, release
|
|
109
|
+
arcobi-sdk fetch --streams 1001,1002 --names demand,price \
|
|
110
|
+
--start 2024-01-01 --end 2024-01-08 --out week.csv
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Exit codes are stable (0 ok, 2 bad arguments or config, 3 vendor or auth error, 4 no data) and
|
|
114
|
+
errors are one `error: ...` line on stderr, so a VBA `Shell` call can act on them. For Excel,
|
|
115
|
+
`ArcobiSdk.bas` is a ready-made macro module that runs the CLI and imports the CSV onto a
|
|
116
|
+
sheet. It ships inside the installed package:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python -c "import importlib.resources as r; print(r.files('arcobi_sdk.excel') / 'vba')"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`pip install "arcobi-sdk[excel]"` additionally provides xlwings-based functions (`datahub_fetch`,
|
|
123
|
+
`datahub_fetch_to_sheet`, `datahub_check`) for spill formulas and `RunPython`. The
|
|
124
|
+
[Excel guide](https://sdk.arcobi.com/excel) walks through both paths.
|
|
125
|
+
|
|
126
|
+
## What is in the box
|
|
127
|
+
|
|
128
|
+
| Module | Purpose |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `arcobi_sdk.datahub` | `DatahubClient`, `fetch_streams`, extraction planning, time-grid alignment, gap interpolation |
|
|
131
|
+
| `arcobi_sdk.timeutils` | local-to-UTC conversion, timestamp naming |
|
|
132
|
+
| `arcobi_sdk.io` | `to_csv`, `to_parquet`, `to_json`, timestamped output folders |
|
|
133
|
+
| `arcobi_sdk.pool` | credential-pool client, priorities, wire protocol; server behind an extra |
|
|
134
|
+
| `arcobi_sdk.cli` | `arcobi-sdk fetch / check / version` command line |
|
|
135
|
+
| `arcobi_sdk.excel` | Excel helpers: value conversion, xlwings UDFs (extra `excel`), VBA module |
|
|
136
|
+
|
|
137
|
+
## Documentation
|
|
138
|
+
|
|
139
|
+
**<https://sdk.arcobi.com>** -- the guides referred to above, plus the API reference generated
|
|
140
|
+
from these docstrings:
|
|
141
|
+
|
|
142
|
+
| Guide | |
|
|
143
|
+
|---|---|
|
|
144
|
+
| [Installation](https://sdk.arcobi.com/installation) | extras, supported versions |
|
|
145
|
+
| [Quick start](https://sdk.arcobi.com/quickstart) | first extraction end to end |
|
|
146
|
+
| [Credentials](https://sdk.arcobi.com/credentials) | every way to authenticate, and the live smoke test |
|
|
147
|
+
| [Credential pool](https://sdk.arcobi.com/examples/credential-pool) | running and deploying the pool service |
|
|
148
|
+
| [Excel](https://sdk.arcobi.com/excel) | the VBA module and the xlwings UDFs |
|
|
149
|
+
|
|
150
|
+
The same material ships in the source distribution on PyPI, under `docs/` and
|
|
151
|
+
`deploy/pool-server/`. A wheel install carries the package only.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
uv sync --all-extras --dev
|
|
157
|
+
uv run pytest
|
|
158
|
+
uv run ruff check . && uv run ruff format --check .
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Tests do not need a database or network access. Tests marked `postgres` run only when
|
|
162
|
+
`POOL_TEST_DATABASE_URL` points at a reachable PostgreSQL. Tests marked `live` call the real
|
|
163
|
+
Datahub API and run only with `DATAHUB_LIVE=1`, credentials, and `DATAHUB_TEST_STREAM_ID`
|
|
164
|
+
set (`uv run pytest -m live`).
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# arcobi-sdk
|
|
2
|
+
|
|
3
|
+
Load energy-market time-series data from Datahub, align it on a time grid, and export it to a
|
|
4
|
+
DataFrame, CSV, Parquet or JSON. Optionally share a pool of vendor credentials across many jobs
|
|
5
|
+
through a small lease service. It takes you from the API to a clean, aligned frame or file,
|
|
6
|
+
ready for the features and models you build on top.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install arcobi-sdk # extraction + export + pool client
|
|
10
|
+
pip install "arcobi-sdk[parquet]" # + Parquet export
|
|
11
|
+
pip install "arcobi-sdk[pool-server]" # + the credential-pool service
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from datetime import datetime, timedelta
|
|
18
|
+
|
|
19
|
+
from arcobi_sdk import DatahubClient, fetch_streams, to_csv
|
|
20
|
+
|
|
21
|
+
end = datetime.now()
|
|
22
|
+
start = end - timedelta(days=7)
|
|
23
|
+
|
|
24
|
+
with DatahubClient(username="...", password="...") as client:
|
|
25
|
+
df = fetch_streams(
|
|
26
|
+
client, streams=[1001, 1002], start=start, end=end, names=["demand", "price"]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
to_csv(df, "week.csv")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`fetch_streams` fetches the streams one after another. To fetch in parallel, share the same
|
|
33
|
+
client across threads: it is thread-safe and holds the account's single token. Never open a
|
|
34
|
+
second client with the same account, Datahub allows one session per account (see the
|
|
35
|
+
[credentials guide](https://sdk.arcobi.com/credentials)).
|
|
36
|
+
|
|
37
|
+
Credentials can also come from the environment (`DATAHUB_USERNAME` / `DATAHUB_PASSWORD`,
|
|
38
|
+
or a pre-issued `DATAHUB_BEARER_TOKEN`), or from a local `.env` file loaded with
|
|
39
|
+
`arcobi_sdk.load_env()`. The [credentials guide](https://sdk.arcobi.com/credentials) covers every
|
|
40
|
+
option and the live smoke test.
|
|
41
|
+
|
|
42
|
+
## Sharing credentials across jobs
|
|
43
|
+
|
|
44
|
+
If several processes share a handful of vendor accounts, run the pool service once and let each
|
|
45
|
+
job lease a token. Leases carry a generic **priority level**: `0` is served first, then `1`, then
|
|
46
|
+
`2`; the default names are `high`, `normal`, `low` and deployments may rename them.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from arcobi_sdk import DatahubClient, fetch_streams
|
|
50
|
+
from arcobi_sdk.pool import PoolClient
|
|
51
|
+
|
|
52
|
+
pool = PoolClient("http://localhost:8002", api_key="...")
|
|
53
|
+
with pool.lease(holder_id="nightly-report", priority="high") as lease:
|
|
54
|
+
with DatahubClient(token_provider=lease.token_provider) as client:
|
|
55
|
+
df = fetch_streams(client, [1001], start, end)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The token is refreshed by the service before it expires and the client picks the new one up
|
|
59
|
+
mid-extraction. Running the service is described in the
|
|
60
|
+
[credential-pool guide](https://sdk.arcobi.com/examples/credential-pool).
|
|
61
|
+
|
|
62
|
+
## Command line and Excel
|
|
63
|
+
|
|
64
|
+
Every install also provides a small CLI, handy for scripts, schedulers and spreadsheet macros:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
arcobi-sdk check # authenticate, print token expiry, release
|
|
68
|
+
arcobi-sdk fetch --streams 1001,1002 --names demand,price \
|
|
69
|
+
--start 2024-01-01 --end 2024-01-08 --out week.csv
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Exit codes are stable (0 ok, 2 bad arguments or config, 3 vendor or auth error, 4 no data) and
|
|
73
|
+
errors are one `error: ...` line on stderr, so a VBA `Shell` call can act on them. For Excel,
|
|
74
|
+
`ArcobiSdk.bas` is a ready-made macro module that runs the CLI and imports the CSV onto a
|
|
75
|
+
sheet. It ships inside the installed package:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -c "import importlib.resources as r; print(r.files('arcobi_sdk.excel') / 'vba')"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`pip install "arcobi-sdk[excel]"` additionally provides xlwings-based functions (`datahub_fetch`,
|
|
82
|
+
`datahub_fetch_to_sheet`, `datahub_check`) for spill formulas and `RunPython`. The
|
|
83
|
+
[Excel guide](https://sdk.arcobi.com/excel) walks through both paths.
|
|
84
|
+
|
|
85
|
+
## What is in the box
|
|
86
|
+
|
|
87
|
+
| Module | Purpose |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `arcobi_sdk.datahub` | `DatahubClient`, `fetch_streams`, extraction planning, time-grid alignment, gap interpolation |
|
|
90
|
+
| `arcobi_sdk.timeutils` | local-to-UTC conversion, timestamp naming |
|
|
91
|
+
| `arcobi_sdk.io` | `to_csv`, `to_parquet`, `to_json`, timestamped output folders |
|
|
92
|
+
| `arcobi_sdk.pool` | credential-pool client, priorities, wire protocol; server behind an extra |
|
|
93
|
+
| `arcobi_sdk.cli` | `arcobi-sdk fetch / check / version` command line |
|
|
94
|
+
| `arcobi_sdk.excel` | Excel helpers: value conversion, xlwings UDFs (extra `excel`), VBA module |
|
|
95
|
+
|
|
96
|
+
## Documentation
|
|
97
|
+
|
|
98
|
+
**<https://sdk.arcobi.com>** -- the guides referred to above, plus the API reference generated
|
|
99
|
+
from these docstrings:
|
|
100
|
+
|
|
101
|
+
| Guide | |
|
|
102
|
+
|---|---|
|
|
103
|
+
| [Installation](https://sdk.arcobi.com/installation) | extras, supported versions |
|
|
104
|
+
| [Quick start](https://sdk.arcobi.com/quickstart) | first extraction end to end |
|
|
105
|
+
| [Credentials](https://sdk.arcobi.com/credentials) | every way to authenticate, and the live smoke test |
|
|
106
|
+
| [Credential pool](https://sdk.arcobi.com/examples/credential-pool) | running and deploying the pool service |
|
|
107
|
+
| [Excel](https://sdk.arcobi.com/excel) | the VBA module and the xlwings UDFs |
|
|
108
|
+
|
|
109
|
+
The same material ships in the source distribution on PyPI, under `docs/` and
|
|
110
|
+
`deploy/pool-server/`. A wheel install carries the package only.
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv sync --all-extras --dev
|
|
116
|
+
uv run pytest
|
|
117
|
+
uv run ruff check . && uv run ruff format --check .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Tests do not need a database or network access. Tests marked `postgres` run only when
|
|
121
|
+
`POOL_TEST_DATABASE_URL` points at a reachable PostgreSQL. Tests marked `live` call the real
|
|
122
|
+
Datahub API and run only with `DATAHUB_LIVE=1`, credentials, and `DATAHUB_TEST_STREAM_ID`
|
|
123
|
+
set (`uv run pytest -m live`).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Credential-pool server: arcobi-sdk with the [pool-server] extra.
|
|
2
|
+
# Build from the repository root: docker build -f deploy/pool-server/Dockerfile -t arcobi-pool-server .
|
|
3
|
+
FROM python:3.12-slim
|
|
4
|
+
|
|
5
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
6
|
+
PYTHONUNBUFFERED=1 \
|
|
7
|
+
PIP_NO_CACHE_DIR=1 \
|
|
8
|
+
POOL_HOST=0.0.0.0 \
|
|
9
|
+
POOL_PORT=8002
|
|
10
|
+
|
|
11
|
+
RUN apt-get update \
|
|
12
|
+
&& apt-get install -y --no-install-recommends curl \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
14
|
+
&& useradd --create-home --uid 10001 pool
|
|
15
|
+
|
|
16
|
+
WORKDIR /build
|
|
17
|
+
|
|
18
|
+
# Copy the sources first so the install sees the whole package.
|
|
19
|
+
COPY pyproject.toml README.md ./
|
|
20
|
+
COPY src ./src
|
|
21
|
+
RUN pip install --upgrade pip && pip install "./[pool-server]" && rm -rf /build
|
|
22
|
+
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
USER pool
|
|
25
|
+
|
|
26
|
+
HEALTHCHECK --interval=15s --timeout=5s --retries=5 \
|
|
27
|
+
CMD curl -fsS "http://127.0.0.1:${POOL_PORT}/health" || exit 1
|
|
28
|
+
|
|
29
|
+
EXPOSE 8002
|
|
30
|
+
CMD ["python", "-m", "arcobi_sdk.pool.server"]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Credential-pool server
|
|
2
|
+
|
|
3
|
+
A small FastAPI service that leases short-lived vendor tokens from a shared pool
|
|
4
|
+
of accounts so several jobs can share a handful of credentials fairly. It ships
|
|
5
|
+
in `arcobi-sdk` behind the `pool-server` extra; the client is in the default
|
|
6
|
+
install.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install "arcobi-sdk[pool-server]"
|
|
10
|
+
export POOL_DATABASE_URL=postgresql://pool:secret@localhost:5432/pool
|
|
11
|
+
export POOL_API_KEY=$(openssl rand -hex 24)
|
|
12
|
+
export POOL_CREDENTIALS_PATH=./credentials_pool.yaml
|
|
13
|
+
python -m arcobi_sdk.pool.server
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or with Docker, from the repository root:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
export POOL_API_KEY=$(openssl rand -hex 24) POOL_DB_PASSWORD=$(openssl rand -hex 16)
|
|
20
|
+
cp deploy/pool-server/credentials_pool.example.yaml deploy/pool-server/credentials_pool.yaml
|
|
21
|
+
docker compose -f deploy/pool-server/docker-compose.yml up --build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
All settings are environment variables prefixed `POOL_`.
|
|
27
|
+
|
|
28
|
+
| Variable | Default | Meaning |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `POOL_DATABASE_URL` | required | PostgreSQL DSN. Schema is created/migrated on start. |
|
|
31
|
+
| `POOL_API_KEY` | required | Shared secret; clients send it as `X-API-Key`. |
|
|
32
|
+
| `POOL_HOST` / `POOL_PORT` | `0.0.0.0` / `8002` | Listener. |
|
|
33
|
+
| `POOL_PROVIDER` | `datahub` | Credential provider (`datahub`, `static`, or an entry point). |
|
|
34
|
+
| `POOL_PROVIDER_OPTIONS` | `{}` | JSON keyword options for the provider, e.g. `{"timeout_seconds": 10}`. |
|
|
35
|
+
| `POOL_CREDENTIALS_PATH` | unset | YAML file upserted on start (see `credentials_pool.example.yaml`). |
|
|
36
|
+
| `POOL_REFRESH_BUFFER_SECONDS` | `90` | Refresh tokens this long before they expire. |
|
|
37
|
+
| `POOL_REFRESH_RETRY_SECONDS` | `30` | Delay between failed refresh attempts. |
|
|
38
|
+
| `POOL_HEARTBEAT_TIMEOUT_SECONDS` | `60` | Reclaim a lease whose last `ping` is older than this. |
|
|
39
|
+
| `POOL_HANDOFF_GRACE_SECONDS` | `15` | Wait for `token_ack` this long before revoking the old token. |
|
|
40
|
+
| `POOL_MAX_QUEUE_DEPTH` | `50` | Queued requests before `POST /leases` answers 503. |
|
|
41
|
+
| `POOL_PRIORITY_LEVELS` | `high,normal,low` | Level names, most urgent first (level 0). |
|
|
42
|
+
| `POOL_PRIORITY_DEFAULT` | `normal` | Level used when a request has no priority. |
|
|
43
|
+
| `POOL_PRIORITY_ALIASES` | unset | Extra names, e.g. `urgent:0,batch:2`. |
|
|
44
|
+
| `POOL_ALLOW_WS_QUERY_API_KEY` | `false` | Also accept `?api_key=` on the WebSocket URL (legacy clients). |
|
|
45
|
+
| `POOL_METRICS` | `noop` | `noop` or `logging`; other sinks via `create_app(metrics=...)`. |
|
|
46
|
+
| `POOL_LOG_LEVEL` | `INFO` | Python log level. |
|
|
47
|
+
|
|
48
|
+
## Priorities
|
|
49
|
+
|
|
50
|
+
A priority is an integer level; `0` is served first and requests within a level
|
|
51
|
+
are first-in-first-out. Clients may send the integer or a level name
|
|
52
|
+
(`"high"`), and a newcomer never overtakes queued requests of equal or higher
|
|
53
|
+
urgency. Rename or add levels per deployment with the `POOL_PRIORITY_*`
|
|
54
|
+
settings; the SDK ships no domain vocabulary.
|
|
55
|
+
|
|
56
|
+
## Endpoints
|
|
57
|
+
|
|
58
|
+
All REST endpoints except `/health` require the `X-API-Key` header.
|
|
59
|
+
|
|
60
|
+
| Method | Path | Result |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `POST` | `/leases` | `201` `status="granted"` with `token`, `expires_at`, `token_version`, `ws_url`; or `202` `status="queued"` with a real `lease_id`, `queue_position`, `estimated_wait_seconds`. `409` if the holder is already active or queued, `422` for an unknown priority, `503` if the queue is full or the vendor is down. |
|
|
63
|
+
| `GET` | `/leases/{lease_id}` | Current state including the token once granted (lets a client that connects late pick it up). |
|
|
64
|
+
| `DELETE` | `/leases/{lease_id}` | Release; `204` even when already finished, `404` if unknown. |
|
|
65
|
+
| `GET` | `/status` | Pool size, active/available credentials, queue depth per level name. |
|
|
66
|
+
| `GET` | `/health` | `status`, `db`, real `uptime_seconds`, `timestamp`. |
|
|
67
|
+
|
|
68
|
+
WebSocket `/ws/{lease_id}` (API key in the handshake header):
|
|
69
|
+
|
|
70
|
+
* server to client: `lease_granted`, `token_refreshed` (both carry `token`,
|
|
71
|
+
`token_type`, `expires_at`, `version`), `token_refresh_failed`,
|
|
72
|
+
`queue_position`, `lease_reclaimed`, `service_shutdown`, `pong`;
|
|
73
|
+
* client to server: `ping` (persists the heartbeat, answered with `pong`),
|
|
74
|
+
`token_ack` (`version`), `checkin` (release and close).
|
|
75
|
+
|
|
76
|
+
Lifecycle: a free credential is leased immediately; otherwise stale leases
|
|
77
|
+
(no heartbeat for `POOL_HEARTBEAT_TIMEOUT_SECONDS`) are reclaimed and retried,
|
|
78
|
+
and failing that the request is persisted as `queued`. A dispatcher grants
|
|
79
|
+
queued requests in priority order whenever a lease is released or reclaimed and
|
|
80
|
+
pushes `lease_granted`. Each active lease is refreshed before expiry; the old
|
|
81
|
+
token is revoked after the client's `token_ack` or the grace period.
|
|
82
|
+
|
|
83
|
+
## Client
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from arcobi_sdk.pool import PoolClient
|
|
87
|
+
|
|
88
|
+
pool = PoolClient(
|
|
89
|
+
"http://localhost:8002", api_key="..."
|
|
90
|
+
) # or ARCOBI_POOL_URL / ARCOBI_POOL_API_KEY
|
|
91
|
+
with pool.lease("job-42", priority="high", max_wait_seconds=300) as lease:
|
|
92
|
+
headers = lease.auth_header() # always the latest token; refreshes arrive over the WebSocket
|
|
93
|
+
...
|
|
94
|
+
# leaving the block releases the lease (DELETE /leases/{id})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Custom providers
|
|
98
|
+
|
|
99
|
+
Implement `arcobi_sdk.pool.server.providers.base.CredentialProvider` and register
|
|
100
|
+
it under the `arcobi_sdk.pool.providers` entry-point group; then set
|
|
101
|
+
`POOL_PROVIDER` to its name. The `static` provider issues fake tokens and is
|
|
102
|
+
meant for tests and local development only.
|
|
103
|
+
|
|
104
|
+
## Tests
|
|
105
|
+
|
|
106
|
+
The default suite runs on an in-memory store with the static provider. Store
|
|
107
|
+
tests against PostgreSQL are marked `postgres` and run only when
|
|
108
|
+
`POOL_TEST_DATABASE_URL` points at a reachable database.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Credentials the pool server leases out.
|
|
2
|
+
#
|
|
3
|
+
# Copy to credentials_pool.yaml, fill in your vendor accounts and point
|
|
4
|
+
# POOL_CREDENTIALS_PATH at it. The file is re-read on every start with upsert
|
|
5
|
+
# semantics: existing accounts keep their id and lease history, changed
|
|
6
|
+
# passwords are updated in place, and accounts removed from the file are
|
|
7
|
+
# marked inactive (never deleted).
|
|
8
|
+
#
|
|
9
|
+
# Every key other than `username`, `provider` and `active` becomes part of the
|
|
10
|
+
# credential's secret. The datahub provider needs `password`.
|
|
11
|
+
|
|
12
|
+
credentials:
|
|
13
|
+
- username: "user_1"
|
|
14
|
+
password: "change-me"
|
|
15
|
+
|
|
16
|
+
- username: "user_2"
|
|
17
|
+
password: "change-me"
|
|
18
|
+
|
|
19
|
+
# Optional fields:
|
|
20
|
+
# - username: "user_3"
|
|
21
|
+
# password: "change-me"
|
|
22
|
+
# provider: datahub # defaults to POOL_PROVIDER; other providers' entries are skipped
|
|
23
|
+
# active: false # keep the row but never lease it
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Local stack: PostgreSQL + the pool server.
|
|
2
|
+
#
|
|
3
|
+
# export POOL_API_KEY=$(openssl rand -hex 24)
|
|
4
|
+
# export POOL_DB_PASSWORD=$(openssl rand -hex 16)
|
|
5
|
+
# cp credentials_pool.example.yaml credentials_pool.yaml # then fill in real accounts
|
|
6
|
+
# docker compose -f deploy/pool-server/docker-compose.yml up --build
|
|
7
|
+
#
|
|
8
|
+
# No secret has a default: compose refuses to start until POOL_API_KEY and
|
|
9
|
+
# POOL_DB_PASSWORD are set in the environment or an .env file next to this file.
|
|
10
|
+
|
|
11
|
+
services:
|
|
12
|
+
postgres:
|
|
13
|
+
image: postgres:16-alpine
|
|
14
|
+
environment:
|
|
15
|
+
POSTGRES_USER: pool
|
|
16
|
+
POSTGRES_PASSWORD: ${POOL_DB_PASSWORD:?required}
|
|
17
|
+
POSTGRES_DB: pool
|
|
18
|
+
volumes:
|
|
19
|
+
- pgdata:/var/lib/postgresql/data
|
|
20
|
+
healthcheck:
|
|
21
|
+
test: ["CMD-SHELL", "pg_isready -U pool -d pool"]
|
|
22
|
+
interval: 5s
|
|
23
|
+
timeout: 3s
|
|
24
|
+
retries: 10
|
|
25
|
+
restart: unless-stopped
|
|
26
|
+
|
|
27
|
+
pool-server:
|
|
28
|
+
build:
|
|
29
|
+
context: ../..
|
|
30
|
+
dockerfile: deploy/pool-server/Dockerfile
|
|
31
|
+
ports:
|
|
32
|
+
- "${POOL_PORT:-8002}:8002"
|
|
33
|
+
environment:
|
|
34
|
+
POOL_DATABASE_URL: postgresql://pool:${POOL_DB_PASSWORD:?required}@postgres:5432/pool
|
|
35
|
+
POOL_API_KEY: ${POOL_API_KEY:?required}
|
|
36
|
+
POOL_PORT: "8002"
|
|
37
|
+
POOL_PROVIDER: ${POOL_PROVIDER:-datahub}
|
|
38
|
+
POOL_CREDENTIALS_PATH: /app/credentials_pool.yaml
|
|
39
|
+
POOL_REFRESH_BUFFER_SECONDS: ${POOL_REFRESH_BUFFER_SECONDS:-90}
|
|
40
|
+
POOL_HEARTBEAT_TIMEOUT_SECONDS: ${POOL_HEARTBEAT_TIMEOUT_SECONDS:-60}
|
|
41
|
+
POOL_HANDOFF_GRACE_SECONDS: ${POOL_HANDOFF_GRACE_SECONDS:-15}
|
|
42
|
+
POOL_MAX_QUEUE_DEPTH: ${POOL_MAX_QUEUE_DEPTH:-50}
|
|
43
|
+
POOL_LOG_LEVEL: ${POOL_LOG_LEVEL:-INFO}
|
|
44
|
+
POOL_METRICS: ${POOL_METRICS:-noop}
|
|
45
|
+
volumes:
|
|
46
|
+
- ./credentials_pool.yaml:/app/credentials_pool.yaml:ro
|
|
47
|
+
depends_on:
|
|
48
|
+
postgres:
|
|
49
|
+
condition: service_healthy
|
|
50
|
+
restart: unless-stopped
|
|
51
|
+
|
|
52
|
+
volumes:
|
|
53
|
+
pgdata:
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Configuring Datahub credentials
|
|
2
|
+
|
|
3
|
+
The SDK never stores credentials itself. It looks for them in this order:
|
|
4
|
+
|
|
5
|
+
1. **Constructor arguments** on `DatahubClient`:
|
|
6
|
+
`DatahubClient(username="...", password="...")` or `DatahubClient(bearer_token="...")`.
|
|
7
|
+
2. **Environment variables**: `DATAHUB_USERNAME` + `DATAHUB_PASSWORD`, or `DATAHUB_BEARER_TOKEN`.
|
|
8
|
+
3. **A leased token** from the credential pool: `DatahubClient(token_provider=lease.token_provider)`
|
|
9
|
+
(see the README section on sharing credentials).
|
|
10
|
+
|
|
11
|
+
If none is found, constructing a client raises `ConfigurationError`.
|
|
12
|
+
|
|
13
|
+
## Environment variables
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# bash / zsh
|
|
17
|
+
export DATAHUB_USERNAME="your-user"
|
|
18
|
+
export DATAHUB_PASSWORD="your-password"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```powershell
|
|
22
|
+
# PowerShell
|
|
23
|
+
$env:DATAHUB_USERNAME = "your-user"
|
|
24
|
+
$env:DATAHUB_PASSWORD = "your-password"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## A `.env` file for local work
|
|
28
|
+
|
|
29
|
+
Create a `.env` file next to your script and keep it out of source control:
|
|
30
|
+
|
|
31
|
+
```ini
|
|
32
|
+
DATAHUB_USERNAME=
|
|
33
|
+
DATAHUB_PASSWORD=
|
|
34
|
+
# Alternatively a pre-issued token:
|
|
35
|
+
# DATAHUB_BEARER_TOKEN=
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Load it one of two ways:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from arcobi_sdk import load_env, DatahubClient
|
|
42
|
+
|
|
43
|
+
load_env() # reads ./.env; never overrides variables already set
|
|
44
|
+
client = DatahubClient()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv run --env-file .env python my_script.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`load_env` is dependency-free and accepts `export KEY=VALUE`, quoted values and `#` comments.
|
|
52
|
+
Pass `override=True` to let the file replace variables that are already set.
|
|
53
|
+
|
|
54
|
+
## One session per account (concurrency)
|
|
55
|
+
|
|
56
|
+
Datahub allows **one active session per account**. A second login with the same credentials
|
|
57
|
+
is rejected with "Another user is currently using the API" (the SDK raises `DatahubAuthError`).
|
|
58
|
+
Consequences:
|
|
59
|
+
|
|
60
|
+
- To fetch several streams in parallel, share **one** `DatahubClient` across threads. The
|
|
61
|
+
client is thread-safe: token acquisition, refresh and release are serialised, so all threads
|
|
62
|
+
reuse the single token. Verified live: three streams fetched by three threads through one
|
|
63
|
+
client return exactly the same data as a sequential fetch.
|
|
64
|
+
- Do not create one client per thread or per process with the same account. For many parallel
|
|
65
|
+
jobs use several accounts through the credential pool, where each job leases its own account.
|
|
66
|
+
- Always release the token when done (`with DatahubClient() as client:` does this), otherwise
|
|
67
|
+
the account stays busy until the vendor-side session expires.
|
|
68
|
+
|
|
69
|
+
## Pool client
|
|
70
|
+
|
|
71
|
+
| Variable | Purpose |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `ARCOBI_POOL_URL` | Base URL of the credential-pool service |
|
|
74
|
+
| `ARCOBI_POOL_API_KEY` | API key sent in the `X-API-Key` header |
|
|
75
|
+
|
|
76
|
+
Both can also be passed to `PoolClient(service_url, api_key=...)`.
|
|
77
|
+
|
|
78
|
+
## Running the live test
|
|
79
|
+
|
|
80
|
+
The default test suite never touches the network. A live smoke test exists and runs only when
|
|
81
|
+
you opt in:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
DATAHUB_LIVE=1 DATAHUB_TEST_STREAM_ID=<id> DATAHUB_TEST_STREAM_IDS=<id1>,<id2>,<id3> uv run pytest -m live
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`DATAHUB_TEST_STREAM_ID` drives the single-stream smoke test; `DATAHUB_TEST_STREAM_IDS`
|
|
88
|
+
(three ids) drives the concurrency test, which also asserts the one-session-per-account rule.
|
|
89
|
+
|
|
90
|
+
With a `.env` file in the repo root the credentials are picked up automatically; the stream id
|
|
91
|
+
can also go in that file. The test authenticates, fetches the last two days of the stream and
|
|
92
|
+
writes a CSV to a temporary directory.
|
|
93
|
+
|
|
94
|
+
## Good practice
|
|
95
|
+
|
|
96
|
+
- Keep credentials out of source control; never commit a filled-in `.env`.
|
|
97
|
+
- Prefer one account per job, or the credential pool, over sharing one login across processes.
|
|
98
|
+
- Rotate a password that has been pasted into a chat, ticket or log.
|