periplus-python-sdk 0.5.0__tar.gz → 0.6.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.
Files changed (24) hide show
  1. {periplus_python_sdk-0.5.0/src/periplus_python_sdk.egg-info → periplus_python_sdk-0.6.0}/PKG-INFO +18 -21
  2. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/README.md +16 -17
  3. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/pyproject.toml +3 -3
  4. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0/src/periplus_python_sdk.egg-info}/PKG-INFO +18 -21
  5. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_python_sdk.egg-info/SOURCES.txt +3 -1
  6. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_python_sdk.egg-info/requires.txt +1 -4
  7. periplus_python_sdk-0.6.0/src/periplus_sdk/sql_api.py +25 -0
  8. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/tests/test_notebook.py +2 -2
  9. periplus_python_sdk-0.6.0/tests/test_sql_api.py +38 -0
  10. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/LICENSE +0 -0
  11. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/NOTICE +0 -0
  12. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/setup.cfg +0 -0
  13. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_python_sdk.egg-info/dependency_links.txt +0 -0
  14. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_python_sdk.egg-info/entry_points.txt +0 -0
  15. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_python_sdk.egg-info/top_level.txt +0 -0
  16. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/__init__.py +0 -0
  17. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/client.py +0 -0
  18. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/dbapi.py +0 -0
  19. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/errors.py +0 -0
  20. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/py.typed +0 -0
  21. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/sqlalchemy.py +0 -0
  22. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/src/periplus_sdk/types.py +0 -0
  23. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/tests/test_client.py +0 -0
  24. {periplus_python_sdk-0.5.0 → periplus_python_sdk-0.6.0}/tests/test_dbapi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: periplus-python-sdk
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Read-only Python client for the public Periplus query API
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/elei-io/periplus
@@ -11,10 +11,8 @@ License-File: LICENSE
11
11
  License-File: NOTICE
12
12
  Requires-Dist: httpx>=0.28
13
13
  Requires-Dist: pydantic<3,>=2.12
14
- Provides-Extra: sqlalchemy
15
- Requires-Dist: sqlalchemy<3,>=2.0; extra == "sqlalchemy"
14
+ Requires-Dist: sqlalchemy<3,>=2.0
16
15
  Provides-Extra: notebook
17
- Requires-Dist: sqlalchemy<3,>=2.0; extra == "notebook"
18
16
  Requires-Dist: marimo[sql]>=0.24.1; extra == "notebook"
19
17
  Dynamic: license-file
20
18
 
@@ -45,18 +43,15 @@ The client reuses HTTP connections; close it with a context manager or `close()`
45
43
  Install the notebook integration from PyPI:
46
44
 
47
45
  ```sh
48
- uv add "periplus-python-sdk[notebook]>=0.5.0"
46
+ uv add "periplus-python-sdk[notebook]>=0.6.0"
49
47
  ```
50
48
 
51
49
  In a Python setup cell, create a SQLAlchemy engine:
52
50
 
53
51
  ```python
54
- from sqlalchemy import create_engine
52
+ from periplus_sdk import sql_api
55
53
 
56
- pp = create_engine(
57
- "periplus:///public_v1",
58
- connect_args={"base_url": "https://periplus.dev", "mode": "stable"},
59
- )
54
+ pp = sql_api.create_engine("https://periplus.dev", mode="stable")
60
55
  ```
61
56
 
62
57
  Add a SQL cell, select **pp** in its connection dropdown, and enter:
@@ -86,16 +81,18 @@ captures = mo.sql(
86
81
  )
87
82
  ```
88
83
 
89
- Set `mode="experimental"` in `connect_args` for the experimental service. The URL
90
- path names the public schema; the HTTPS endpoint belongs in `base_url` (or set
91
- `PERIPLUS_PUBLIC_URL`). Run `pp.dispose()` when finished. This is a read-only
84
+ Set `mode="experimental"` for the experimental service. Omit the URL to use
85
+ `PERIPLUS_PUBLIC_URL`. Optional `timeout=140` and `schema_version="public_v1"`
86
+ arguments configure the client deadline and public schema. Run `pp.dispose()` when finished. This is a read-only
92
87
  SQLAlchemy dialect for textual SQL and reflection, not a writable ORM backend.
93
88
  Each statement has its own server snapshot; SQLAlchemy transaction blocks do not
94
89
  provide a shared snapshot or rollback. The adapter makes no transaction requests.
95
90
 
96
91
  A complete notebook is in `examples/notebook.py`. The integration is tested with
97
- marimo 0.24.1 and SQLAlchemy 2.x. For SQLAlchemy without marimo, install the
98
- `sqlalchemy` extra instead of `notebook`.
92
+ marimo 0.24.1 and SQLAlchemy 2.x. SQLAlchemy is included in the standard SDK install; the `notebook` extra adds
93
+ marimo. Existing marimo environments only need `uv add "periplus-python-sdk>=0.6.0"`.
94
+ The returned object is a standard SQLAlchemy Engine, also usable with pandas and
95
+ ordinary Python scripts. Engine creation is lazy; the first query opens a connection.
99
96
 
100
97
  ## DB-API connection
101
98
 
@@ -199,10 +196,10 @@ Use `aclose()` when managing an async client's lifetime explicitly.
199
196
  Install the public-v1 client from PyPI:
200
197
 
201
198
  ```sh
202
- python -m pip install "periplus-python-sdk>=0.5.0"
199
+ python -m pip install "periplus-python-sdk>=0.6.0"
203
200
  ```
204
201
 
205
- Version 0.5.0 supports the current public-v1 contract. For production, configure
202
+ Version 0.6.0 supports the current public-v1 contract. For production, configure
206
203
  `PERIPLUS_PUBLIC_URL=https://periplus.dev`; no API token is required.
207
204
  Run the installed package against an available public app:
208
205
 
@@ -214,11 +211,11 @@ PERIPLUS_PUBLIC_URL=http://localhost:8080 python packages/periplus-python-sdk/ex
214
211
 
215
212
  Repository CI publishes immutable releases from tags named
216
213
  `periplus-python-sdk-v<version>`. The tag must exactly match the static version
217
- in `pyproject.toml`; for example, version `0.5.0` is released with:
214
+ in `pyproject.toml`; for example, version `0.6.0` is released with:
218
215
 
219
216
  ```sh
220
- git tag periplus-python-sdk-v0.5.0
221
- git push origin periplus-python-sdk-v0.5.0
217
+ git tag periplus-python-sdk-v0.6.0
218
+ git push origin periplus-python-sdk-v0.6.0
222
219
  ```
223
220
 
224
221
  PyPI publishing uses Trusted Publishing rather than a stored API token. The
@@ -228,7 +225,7 @@ that GitHub environment with required reviewers before the first release.
228
225
 
229
226
  ## Public v1
230
227
 
231
- Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.5.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
228
+ Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.6.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
232
229
 
233
230
  ## License
234
231
 
@@ -25,18 +25,15 @@ The client reuses HTTP connections; close it with a context manager or `close()`
25
25
  Install the notebook integration from PyPI:
26
26
 
27
27
  ```sh
28
- uv add "periplus-python-sdk[notebook]>=0.5.0"
28
+ uv add "periplus-python-sdk[notebook]>=0.6.0"
29
29
  ```
30
30
 
31
31
  In a Python setup cell, create a SQLAlchemy engine:
32
32
 
33
33
  ```python
34
- from sqlalchemy import create_engine
34
+ from periplus_sdk import sql_api
35
35
 
36
- pp = create_engine(
37
- "periplus:///public_v1",
38
- connect_args={"base_url": "https://periplus.dev", "mode": "stable"},
39
- )
36
+ pp = sql_api.create_engine("https://periplus.dev", mode="stable")
40
37
  ```
41
38
 
42
39
  Add a SQL cell, select **pp** in its connection dropdown, and enter:
@@ -66,16 +63,18 @@ captures = mo.sql(
66
63
  )
67
64
  ```
68
65
 
69
- Set `mode="experimental"` in `connect_args` for the experimental service. The URL
70
- path names the public schema; the HTTPS endpoint belongs in `base_url` (or set
71
- `PERIPLUS_PUBLIC_URL`). Run `pp.dispose()` when finished. This is a read-only
66
+ Set `mode="experimental"` for the experimental service. Omit the URL to use
67
+ `PERIPLUS_PUBLIC_URL`. Optional `timeout=140` and `schema_version="public_v1"`
68
+ arguments configure the client deadline and public schema. Run `pp.dispose()` when finished. This is a read-only
72
69
  SQLAlchemy dialect for textual SQL and reflection, not a writable ORM backend.
73
70
  Each statement has its own server snapshot; SQLAlchemy transaction blocks do not
74
71
  provide a shared snapshot or rollback. The adapter makes no transaction requests.
75
72
 
76
73
  A complete notebook is in `examples/notebook.py`. The integration is tested with
77
- marimo 0.24.1 and SQLAlchemy 2.x. For SQLAlchemy without marimo, install the
78
- `sqlalchemy` extra instead of `notebook`.
74
+ marimo 0.24.1 and SQLAlchemy 2.x. SQLAlchemy is included in the standard SDK install; the `notebook` extra adds
75
+ marimo. Existing marimo environments only need `uv add "periplus-python-sdk>=0.6.0"`.
76
+ The returned object is a standard SQLAlchemy Engine, also usable with pandas and
77
+ ordinary Python scripts. Engine creation is lazy; the first query opens a connection.
79
78
 
80
79
  ## DB-API connection
81
80
 
@@ -179,10 +178,10 @@ Use `aclose()` when managing an async client's lifetime explicitly.
179
178
  Install the public-v1 client from PyPI:
180
179
 
181
180
  ```sh
182
- python -m pip install "periplus-python-sdk>=0.5.0"
181
+ python -m pip install "periplus-python-sdk>=0.6.0"
183
182
  ```
184
183
 
185
- Version 0.5.0 supports the current public-v1 contract. For production, configure
184
+ Version 0.6.0 supports the current public-v1 contract. For production, configure
186
185
  `PERIPLUS_PUBLIC_URL=https://periplus.dev`; no API token is required.
187
186
  Run the installed package against an available public app:
188
187
 
@@ -194,11 +193,11 @@ PERIPLUS_PUBLIC_URL=http://localhost:8080 python packages/periplus-python-sdk/ex
194
193
 
195
194
  Repository CI publishes immutable releases from tags named
196
195
  `periplus-python-sdk-v<version>`. The tag must exactly match the static version
197
- in `pyproject.toml`; for example, version `0.5.0` is released with:
196
+ in `pyproject.toml`; for example, version `0.6.0` is released with:
198
197
 
199
198
  ```sh
200
- git tag periplus-python-sdk-v0.5.0
201
- git push origin periplus-python-sdk-v0.5.0
199
+ git tag periplus-python-sdk-v0.6.0
200
+ git push origin periplus-python-sdk-v0.6.0
202
201
  ```
203
202
 
204
203
  PyPI publishing uses Trusted Publishing rather than a stored API token. The
@@ -208,7 +207,7 @@ that GitHub environment with required reviewers before the first release.
208
207
 
209
208
  ## Public v1
210
209
 
211
- Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.5.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
210
+ Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.6.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
212
211
 
213
212
  ## License
214
213
 
@@ -2,18 +2,18 @@
2
2
  license = "Apache-2.0"
3
3
  license-files = ["LICENSE", "NOTICE"]
4
4
  name = "periplus-python-sdk"
5
- version = "0.5.0"
5
+ version = "0.6.0"
6
6
  description = "Read-only Python client for the public Periplus query API"
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.11"
9
9
  dependencies = [
10
10
  "httpx>=0.28",
11
11
  "pydantic>=2.12,<3",
12
+ "sqlalchemy>=2.0,<3",
12
13
  ]
13
14
 
14
15
  [project.optional-dependencies]
15
- sqlalchemy = ["sqlalchemy>=2.0,<3"]
16
- notebook = ["sqlalchemy>=2.0,<3", "marimo[sql]>=0.24.1"]
16
+ notebook = ["marimo[sql]>=0.24.1"]
17
17
 
18
18
  [project.entry-points."sqlalchemy.dialects"]
19
19
  periplus = "periplus_sdk.sqlalchemy:PeriplusDialect"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: periplus-python-sdk
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Read-only Python client for the public Periplus query API
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/elei-io/periplus
@@ -11,10 +11,8 @@ License-File: LICENSE
11
11
  License-File: NOTICE
12
12
  Requires-Dist: httpx>=0.28
13
13
  Requires-Dist: pydantic<3,>=2.12
14
- Provides-Extra: sqlalchemy
15
- Requires-Dist: sqlalchemy<3,>=2.0; extra == "sqlalchemy"
14
+ Requires-Dist: sqlalchemy<3,>=2.0
16
15
  Provides-Extra: notebook
17
- Requires-Dist: sqlalchemy<3,>=2.0; extra == "notebook"
18
16
  Requires-Dist: marimo[sql]>=0.24.1; extra == "notebook"
19
17
  Dynamic: license-file
20
18
 
@@ -45,18 +43,15 @@ The client reuses HTTP connections; close it with a context manager or `close()`
45
43
  Install the notebook integration from PyPI:
46
44
 
47
45
  ```sh
48
- uv add "periplus-python-sdk[notebook]>=0.5.0"
46
+ uv add "periplus-python-sdk[notebook]>=0.6.0"
49
47
  ```
50
48
 
51
49
  In a Python setup cell, create a SQLAlchemy engine:
52
50
 
53
51
  ```python
54
- from sqlalchemy import create_engine
52
+ from periplus_sdk import sql_api
55
53
 
56
- pp = create_engine(
57
- "periplus:///public_v1",
58
- connect_args={"base_url": "https://periplus.dev", "mode": "stable"},
59
- )
54
+ pp = sql_api.create_engine("https://periplus.dev", mode="stable")
60
55
  ```
61
56
 
62
57
  Add a SQL cell, select **pp** in its connection dropdown, and enter:
@@ -86,16 +81,18 @@ captures = mo.sql(
86
81
  )
87
82
  ```
88
83
 
89
- Set `mode="experimental"` in `connect_args` for the experimental service. The URL
90
- path names the public schema; the HTTPS endpoint belongs in `base_url` (or set
91
- `PERIPLUS_PUBLIC_URL`). Run `pp.dispose()` when finished. This is a read-only
84
+ Set `mode="experimental"` for the experimental service. Omit the URL to use
85
+ `PERIPLUS_PUBLIC_URL`. Optional `timeout=140` and `schema_version="public_v1"`
86
+ arguments configure the client deadline and public schema. Run `pp.dispose()` when finished. This is a read-only
92
87
  SQLAlchemy dialect for textual SQL and reflection, not a writable ORM backend.
93
88
  Each statement has its own server snapshot; SQLAlchemy transaction blocks do not
94
89
  provide a shared snapshot or rollback. The adapter makes no transaction requests.
95
90
 
96
91
  A complete notebook is in `examples/notebook.py`. The integration is tested with
97
- marimo 0.24.1 and SQLAlchemy 2.x. For SQLAlchemy without marimo, install the
98
- `sqlalchemy` extra instead of `notebook`.
92
+ marimo 0.24.1 and SQLAlchemy 2.x. SQLAlchemy is included in the standard SDK install; the `notebook` extra adds
93
+ marimo. Existing marimo environments only need `uv add "periplus-python-sdk>=0.6.0"`.
94
+ The returned object is a standard SQLAlchemy Engine, also usable with pandas and
95
+ ordinary Python scripts. Engine creation is lazy; the first query opens a connection.
99
96
 
100
97
  ## DB-API connection
101
98
 
@@ -199,10 +196,10 @@ Use `aclose()` when managing an async client's lifetime explicitly.
199
196
  Install the public-v1 client from PyPI:
200
197
 
201
198
  ```sh
202
- python -m pip install "periplus-python-sdk>=0.5.0"
199
+ python -m pip install "periplus-python-sdk>=0.6.0"
203
200
  ```
204
201
 
205
- Version 0.5.0 supports the current public-v1 contract. For production, configure
202
+ Version 0.6.0 supports the current public-v1 contract. For production, configure
206
203
  `PERIPLUS_PUBLIC_URL=https://periplus.dev`; no API token is required.
207
204
  Run the installed package against an available public app:
208
205
 
@@ -214,11 +211,11 @@ PERIPLUS_PUBLIC_URL=http://localhost:8080 python packages/periplus-python-sdk/ex
214
211
 
215
212
  Repository CI publishes immutable releases from tags named
216
213
  `periplus-python-sdk-v<version>`. The tag must exactly match the static version
217
- in `pyproject.toml`; for example, version `0.5.0` is released with:
214
+ in `pyproject.toml`; for example, version `0.6.0` is released with:
218
215
 
219
216
  ```sh
220
- git tag periplus-python-sdk-v0.5.0
221
- git push origin periplus-python-sdk-v0.5.0
217
+ git tag periplus-python-sdk-v0.6.0
218
+ git push origin periplus-python-sdk-v0.6.0
222
219
  ```
223
220
 
224
221
  PyPI publishing uses Trusted Publishing rather than a stored API token. The
@@ -228,7 +225,7 @@ that GitHub environment with required reviewers before the first release.
228
225
 
229
226
  ## Public v1
230
227
 
231
- Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.5.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
228
+ Install the updated SDK from PyPI with `python -m pip install "periplus-python-sdk>=0.6.0"`. The previously published 0.2.0 release predates this contract. `prepare` and `execute` accept keyword-only `schema_version="public_v1"` (the default); responses preserve `schema_version` separately from `source_snapshot`. Unavailable versions are rejected by the server.
232
229
 
233
230
  ## License
234
231
 
@@ -13,8 +13,10 @@ src/periplus_sdk/client.py
13
13
  src/periplus_sdk/dbapi.py
14
14
  src/periplus_sdk/errors.py
15
15
  src/periplus_sdk/py.typed
16
+ src/periplus_sdk/sql_api.py
16
17
  src/periplus_sdk/sqlalchemy.py
17
18
  src/periplus_sdk/types.py
18
19
  tests/test_client.py
19
20
  tests/test_dbapi.py
20
- tests/test_notebook.py
21
+ tests/test_notebook.py
22
+ tests/test_sql_api.py
@@ -1,9 +1,6 @@
1
1
  httpx>=0.28
2
2
  pydantic<3,>=2.12
3
+ sqlalchemy<3,>=2.0
3
4
 
4
5
  [notebook]
5
- sqlalchemy<3,>=2.0
6
6
  marimo[sql]>=0.24.1
7
-
8
- [sqlalchemy]
9
- sqlalchemy<3,>=2.0
@@ -0,0 +1,25 @@
1
+ """Notebook-friendly SQL engine over the public Periplus query API."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal
5
+
6
+ from sqlalchemy import create_engine as _create_engine
7
+ from sqlalchemy.engine import Engine, URL
8
+
9
+
10
+ def create_engine(
11
+ base_url: str | None = None,
12
+ *,
13
+ mode: Literal["stable", "experimental"] = "stable",
14
+ timeout: float = 140,
15
+ schema_version: str = "public_v1",
16
+ ) -> Engine:
17
+ """Create a SQLAlchemy engine recognized by marimo and other SQL tools.
18
+
19
+ The public URL defaults to PERIPLUS_PUBLIC_URL. Connections are opened lazily;
20
+ dispose the engine when finished. Each query uses an independent server snapshot.
21
+ """
22
+ return _create_engine(
23
+ URL.create("periplus", database=schema_version),
24
+ connect_args={"base_url": base_url, "mode": mode, "timeout": timeout},
25
+ )
@@ -9,7 +9,7 @@ from test_client import RESULT
9
9
  @unittest.skipUnless(importlib.util.find_spec('sqlalchemy') and importlib.util.find_spec('marimo'), 'notebook extra required')
10
10
  class NotebookTests(unittest.TestCase):
11
11
  def engine(self, *, truncated=False):
12
- from sqlalchemy import create_engine
12
+ from periplus_sdk import sql_api
13
13
  factory = httpx.Client
14
14
  self.requests = []
15
15
  def handler(request):
@@ -27,7 +27,7 @@ class NotebookTests(unittest.TestCase):
27
27
  patcher = patch('periplus_sdk.client.httpx.Client', side_effect=lambda **kw: factory(**kw,transport=httpx.MockTransport(handler)))
28
28
  patcher.start()
29
29
  self.addCleanup(patcher.stop)
30
- engine = create_engine('periplus:///public_v1',connect_args={'base_url':'https://public.example'})
30
+ engine = sql_api.create_engine('https://public.example')
31
31
  self.addCleanup(engine.dispose)
32
32
  return engine
33
33
 
@@ -0,0 +1,38 @@
1
+ import json
2
+ import os
3
+ import unittest
4
+ from unittest.mock import patch
5
+
6
+ import httpx
7
+ from sqlalchemy import inspect, text
8
+ from sqlalchemy.engine import Engine
9
+ from periplus_sdk import sql_api
10
+ from test_client import RESULT
11
+
12
+
13
+ class SQLApiTests(unittest.TestCase):
14
+ def test_engine_options_environment_and_queries(self):
15
+ factory = httpx.Client
16
+ for mode in ('stable', 'experimental'):
17
+ requests = []
18
+ def handler(request):
19
+ requests.append(request)
20
+ return httpx.Response(200, json=dict(RESULT, columns=['n'], types=['INTEGER'], rows=[[42]], truncated=False))
21
+ with self.subTest(mode=mode), patch.dict(os.environ, {'PERIPLUS_PUBLIC_URL':'https://public.example/prefix'}), patch(
22
+ 'periplus_sdk.client.httpx.Client',
23
+ side_effect=lambda **kw: factory(**kw, transport=httpx.MockTransport(handler)),
24
+ ):
25
+ engine = sql_api.create_engine(mode=mode, timeout=37)
26
+ self.assertIsInstance(engine, Engine)
27
+ self.assertEqual(requests, [])
28
+ try:
29
+ self.assertEqual(inspect(engine).default_schema_name, 'public_v1')
30
+ with engine.connect() as conn:
31
+ self.assertEqual(conn.connection.dbapi_connection._client._http.timeout.read,37)
32
+ self.assertEqual(conn.execute(text('SELECT :n AS n'), {'n':42}).fetchall(), [(42,)])
33
+ suffix = 'experimental/' if mode == 'experimental' else ''
34
+ self.assertEqual(requests[0].url.path, '/prefix/api/query/'+suffix+'exec')
35
+ self.assertEqual(json.loads(requests[0].content)['parameters'], [42])
36
+ self.assertEqual(json.loads(requests[0].content)['schema_version'], 'public_v1')
37
+ finally:
38
+ engine.dispose()