bondi 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.
- bondi-0.1.0/.gitignore +24 -0
- bondi-0.1.0/CHANGELOG.md +12 -0
- bondi-0.1.0/CITATION.cff +9 -0
- bondi-0.1.0/LICENSE +21 -0
- bondi-0.1.0/PKG-INFO +100 -0
- bondi-0.1.0/README.md +74 -0
- bondi-0.1.0/pyproject.toml +61 -0
- bondi-0.1.0/src/bondi/__init__.py +1 -0
- bondi-0.1.0/src/bondi/_sync.py +122 -0
- bondi-0.1.0/src/bondi/couchbase/__init__.py +8 -0
- bondi-0.1.0/src/bondi/couchbase/executor.py +376 -0
- bondi-0.1.0/src/bondi/couchbase/explain.py +223 -0
- bondi-0.1.0/src/bondi/couchbase/query.py +187 -0
- bondi-0.1.0/src/bondi/couchbase/sync_executor.py +109 -0
- bondi-0.1.0/src/bondi/estimation.py +42 -0
- bondi-0.1.0/src/bondi/execution.py +58 -0
- bondi-0.1.0/src/bondi/mongodb/__init__.py +28 -0
- bondi-0.1.0/src/bondi/mongodb/_serialization.py +98 -0
- bondi-0.1.0/src/bondi/mongodb/aggregate.py +135 -0
- bondi-0.1.0/src/bondi/mongodb/collection.py +47 -0
- bondi-0.1.0/src/bondi/mongodb/estimator.py +331 -0
- bondi-0.1.0/src/bondi/mongodb/executor.py +380 -0
- bondi-0.1.0/src/bondi/mongodb/explain.py +172 -0
- bondi-0.1.0/src/bondi/mongodb/generation.py +508 -0
- bondi-0.1.0/src/bondi/mongodb/generation_config.py +178 -0
- bondi-0.1.0/src/bondi/mongodb/pattern.py +199 -0
- bondi-0.1.0/src/bondi/mongodb/query.py +188 -0
- bondi-0.1.0/src/bondi/mongodb/sync_executor.py +126 -0
- bondi-0.1.0/src/bondi/query.py +35 -0
- bondi-0.1.0/src/bondi/schema.py +329 -0
- bondi-0.1.0/src/bondi/summary.py +45 -0
- bondi-0.1.0/src/bondi/workload.py +284 -0
- bondi-0.1.0/tests/fixtures/couchbase/README.md +19 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_aggregation.json +138 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_covering.json +115 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_empty.json +112 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_filtered.json +112 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_scalar.json +73 -0
- bondi-0.1.0/tests/fixtures/couchbase/community_sorted.json +131 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_aggregation.json +406 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_covering.json +317 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_empty.json +322 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_filtered.json +336 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_scalar.json +189 -0
- bondi-0.1.0/tests/fixtures/couchbase/enterprise_sorted.json +413 -0
- bondi-0.1.0/tests/fixtures/explain/README.md +17 -0
- bondi-0.1.0/tests/fixtures/explain/aggregate_facet.json +140 -0
- bondi-0.1.0/tests/fixtures/explain/aggregate_group.json +155 -0
- bondi-0.1.0/tests/fixtures/explain/aggregate_lookup.json +91 -0
- bondi-0.1.0/tests/fixtures/explain/aggregate_sort.json +90 -0
- bondi-0.1.0/tests/fixtures/explain/find_collscan.json +75 -0
- bondi-0.1.0/tests/fixtures/explain/find_index.json +123 -0
- bondi-0.1.0/tests/test_couchbase_executor.py +322 -0
- bondi-0.1.0/tests/test_couchbase_executor_live.py +92 -0
- bondi-0.1.0/tests/test_couchbase_explain.py +208 -0
- bondi-0.1.0/tests/test_couchbase_optional.py +33 -0
- bondi-0.1.0/tests/test_couchbase_query.py +165 -0
- bondi-0.1.0/tests/test_executor.py +442 -0
- bondi-0.1.0/tests/test_executor_mongodb.py +70 -0
- bondi-0.1.0/tests/test_explain.py +176 -0
- bondi-0.1.0/tests/test_mongo_generation.py +498 -0
- bondi-0.1.0/tests/test_mongo_generation_live.py +80 -0
- bondi-0.1.0/tests/test_mongodb_aggregate.py +174 -0
- bondi-0.1.0/tests/test_mongodb_collection.py +46 -0
- bondi-0.1.0/tests/test_mongodb_estimator.py +265 -0
- bondi-0.1.0/tests/test_mongodb_estimator_live.py +65 -0
- bondi-0.1.0/tests/test_mongodb_query.py +101 -0
- bondi-0.1.0/tests/test_mongodb_serialization.py +219 -0
- bondi-0.1.0/tests/test_package.py +13 -0
- bondi-0.1.0/tests/test_schema.py +207 -0
- bondi-0.1.0/tests/test_summarize.py +35 -0
- bondi-0.1.0/tests/test_sync_executors.py +216 -0
- bondi-0.1.0/tests/test_sync_notebook_live.py +88 -0
- bondi-0.1.0/tests/test_workload.py +135 -0
- bondi-0.1.0/tests/test_workload_persistence.py +149 -0
- bondi-0.1.0/uv.lock +949 -0
bondi-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python and local environments
|
|
2
|
+
.venv/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.coverage
|
|
9
|
+
htmlcov/
|
|
10
|
+
|
|
11
|
+
# Generated documentation and distributions
|
|
12
|
+
site/
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
|
|
16
|
+
# Local configuration and notebook state
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
!.env.example
|
|
20
|
+
.ipynb_checkpoints/
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# Locally generated research workloads
|
|
24
|
+
/*workload*.jsonl
|
bondi-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — initial release (prepared)
|
|
4
|
+
|
|
5
|
+
- Native MongoDB find/aggregation and Couchbase SQL++ queries with JSONL workloads.
|
|
6
|
+
- Synchronous and asynchronous execution, explain and profile operations.
|
|
7
|
+
- Offline query summaries, schema inspection and optional value retention.
|
|
8
|
+
- MongoDB sampling estimation and pattern-based workload generation.
|
|
9
|
+
- Student notebooks, local Docker environments and documentation.
|
|
10
|
+
|
|
11
|
+
This is an early research release. Public APIs and persistence formats may change
|
|
12
|
+
in subsequent releases; retain the package version alongside experiment artifacts.
|
bondi-0.1.0/CITATION.cff
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use BONDI in research, please cite this software."
|
|
3
|
+
title: "BONDI: A Python toolkit for document database research"
|
|
4
|
+
authors:
|
|
5
|
+
- name: Relaxed Constraints
|
|
6
|
+
version: 0.1.0
|
|
7
|
+
license: MIT
|
|
8
|
+
repository-code: "https://github.com/relaxed-constraints/bondi"
|
|
9
|
+
url: "https://relaxed-constraints.github.io/bondi/"
|
bondi-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Relaxed Constraints
|
|
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.
|
bondi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bondi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python toolkit for document database research
|
|
5
|
+
Project-URL: Homepage, https://github.com/relaxed-constraints/bondi
|
|
6
|
+
Project-URL: Documentation, https://relaxed-constraints.github.io/bondi/
|
|
7
|
+
Project-URL: Repository, https://github.com/relaxed-constraints/bondi
|
|
8
|
+
Project-URL: Issues, https://github.com/relaxed-constraints/bondi/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/relaxed-constraints/bondi/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Relaxed Constraints
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: pymongo<5,>=4.13
|
|
23
|
+
Provides-Extra: couchbase
|
|
24
|
+
Requires-Dist: couchbase<5,>=4.6.3; extra == 'couchbase'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# BONDI — A Python toolkit for document database research
|
|
28
|
+
|
|
29
|
+
**bondi** provides A Python toolkit for document database research.
|
|
30
|
+
|
|
31
|
+
Use native MongoDB and Couchbase queries, execute and profile workloads, inspect
|
|
32
|
+
schemas, estimate MongoDB selectivity, and generate pattern-based workloads.
|
|
33
|
+
Synchronous APIs work in scripts and notebooks; async APIs remain available.
|
|
34
|
+
Research algorithms and experiment tracking stay in separate projects.
|
|
35
|
+
|
|
36
|
+
Start with [installation](docs/installation.md) and the [quickstart](docs/quickstart.md),
|
|
37
|
+
or browse the [documentation overview](docs/index.md).
|
|
38
|
+
|
|
39
|
+
## Start developing
|
|
40
|
+
|
|
41
|
+
Requires [uv](https://docs.astral.sh/uv/) and Python 3.11 or newer. From this directory:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
uv sync --locked
|
|
45
|
+
uv run python -c "import bondi; print(bondi.__file__)"
|
|
46
|
+
uv run pytest
|
|
47
|
+
uv run ruff check .
|
|
48
|
+
uv run ruff format --check .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`uv sync` installs the package in editable mode and includes the development group.
|
|
52
|
+
PyMongo is a required dependency for BSON support; no database service is needed.
|
|
53
|
+
The committed `uv.lock` records the development dependency resolution.
|
|
54
|
+
|
|
55
|
+
[Documentation](https://relaxed-constraints.github.io/bondi/) ·
|
|
56
|
+
[Issues](https://github.com/relaxed-constraints/bondi/issues) · [MIT license](LICENSE)
|
|
57
|
+
|
|
58
|
+
## Read the documentation
|
|
59
|
+
|
|
60
|
+
Start with [the documentation overview](docs/index.md),
|
|
61
|
+
[architecture decisions](docs/architecture.md), and
|
|
62
|
+
[the implementation roadmap](docs/roadmap.md).
|
|
63
|
+
|
|
64
|
+
To preview or validate the documentation site:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
uv sync --locked --group docs
|
|
68
|
+
uv run --group docs mkdocs serve
|
|
69
|
+
# Or build without starting a server:
|
|
70
|
+
uv run --group docs mkdocs build --strict
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
API reference pages are generated from Python docstrings using mkdocstrings.
|
|
74
|
+
Tutorials are available in `notebooks/workload_demo.ipynb` (MongoDB) and
|
|
75
|
+
`notebooks/couchbase_demo.ipynb` (Couchbase).
|
|
76
|
+
For database-independent inference and inspection, see the [schema guide](docs/schemas.md)
|
|
77
|
+
and `notebooks/schema_demo.ipynb`.
|
|
78
|
+
|
|
79
|
+
## Repository layout
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
src/bondi/ Installable library
|
|
83
|
+
tests/ Offline tests
|
|
84
|
+
docs/ Getting started, guides, tutorials, API reference and development
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
See [the contributor guide](docs/contributing.md) for the development workflow.
|
|
88
|
+
A distribution can be built with `uv build`.
|
|
89
|
+
|
|
90
|
+
## Publication
|
|
91
|
+
|
|
92
|
+
BONDI is MIT licensed, copyright Relaxed Constraints. Version 0.1.0 is being
|
|
93
|
+
prepared for PyPI; use the source installation instructions until it is published.
|
|
94
|
+
See [release preparation](docs/releases.md) for the publishing process.
|
|
95
|
+
|
|
96
|
+
## Optional local databases
|
|
97
|
+
|
|
98
|
+
See [the local database guide](docs/local-databases.md) for MongoDB and Couchbase
|
|
99
|
+
Docker Compose profiles, sample data, startup and cleanup. No database or Docker
|
|
100
|
+
installation is required for the offline test suite.
|
bondi-0.1.0/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# BONDI — A Python toolkit for document database research
|
|
2
|
+
|
|
3
|
+
**bondi** provides A Python toolkit for document database research.
|
|
4
|
+
|
|
5
|
+
Use native MongoDB and Couchbase queries, execute and profile workloads, inspect
|
|
6
|
+
schemas, estimate MongoDB selectivity, and generate pattern-based workloads.
|
|
7
|
+
Synchronous APIs work in scripts and notebooks; async APIs remain available.
|
|
8
|
+
Research algorithms and experiment tracking stay in separate projects.
|
|
9
|
+
|
|
10
|
+
Start with [installation](docs/installation.md) and the [quickstart](docs/quickstart.md),
|
|
11
|
+
or browse the [documentation overview](docs/index.md).
|
|
12
|
+
|
|
13
|
+
## Start developing
|
|
14
|
+
|
|
15
|
+
Requires [uv](https://docs.astral.sh/uv/) and Python 3.11 or newer. From this directory:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
uv sync --locked
|
|
19
|
+
uv run python -c "import bondi; print(bondi.__file__)"
|
|
20
|
+
uv run pytest
|
|
21
|
+
uv run ruff check .
|
|
22
|
+
uv run ruff format --check .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`uv sync` installs the package in editable mode and includes the development group.
|
|
26
|
+
PyMongo is a required dependency for BSON support; no database service is needed.
|
|
27
|
+
The committed `uv.lock` records the development dependency resolution.
|
|
28
|
+
|
|
29
|
+
[Documentation](https://relaxed-constraints.github.io/bondi/) ·
|
|
30
|
+
[Issues](https://github.com/relaxed-constraints/bondi/issues) · [MIT license](LICENSE)
|
|
31
|
+
|
|
32
|
+
## Read the documentation
|
|
33
|
+
|
|
34
|
+
Start with [the documentation overview](docs/index.md),
|
|
35
|
+
[architecture decisions](docs/architecture.md), and
|
|
36
|
+
[the implementation roadmap](docs/roadmap.md).
|
|
37
|
+
|
|
38
|
+
To preview or validate the documentation site:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
uv sync --locked --group docs
|
|
42
|
+
uv run --group docs mkdocs serve
|
|
43
|
+
# Or build without starting a server:
|
|
44
|
+
uv run --group docs mkdocs build --strict
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
API reference pages are generated from Python docstrings using mkdocstrings.
|
|
48
|
+
Tutorials are available in `notebooks/workload_demo.ipynb` (MongoDB) and
|
|
49
|
+
`notebooks/couchbase_demo.ipynb` (Couchbase).
|
|
50
|
+
For database-independent inference and inspection, see the [schema guide](docs/schemas.md)
|
|
51
|
+
and `notebooks/schema_demo.ipynb`.
|
|
52
|
+
|
|
53
|
+
## Repository layout
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
src/bondi/ Installable library
|
|
57
|
+
tests/ Offline tests
|
|
58
|
+
docs/ Getting started, guides, tutorials, API reference and development
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
See [the contributor guide](docs/contributing.md) for the development workflow.
|
|
62
|
+
A distribution can be built with `uv build`.
|
|
63
|
+
|
|
64
|
+
## Publication
|
|
65
|
+
|
|
66
|
+
BONDI is MIT licensed, copyright Relaxed Constraints. Version 0.1.0 is being
|
|
67
|
+
prepared for PyPI; use the source installation instructions until it is published.
|
|
68
|
+
See [release preparation](docs/releases.md) for the publishing process.
|
|
69
|
+
|
|
70
|
+
## Optional local databases
|
|
71
|
+
|
|
72
|
+
See [the local database guide](docs/local-databases.md) for MongoDB and Couchbase
|
|
73
|
+
Docker Compose profiles, sample data, startup and cleanup. No database or Docker
|
|
74
|
+
installation is required for the offline test suite.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bondi"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Python toolkit for document database research"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{name = "Relaxed Constraints"}]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 3 - Alpha",
|
|
11
|
+
"Intended Audience :: Science/Research",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
17
|
+
"Topic :: Database",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.11"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pymongo>=4.13,<5",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/relaxed-constraints/bondi"
|
|
26
|
+
Documentation = "https://relaxed-constraints.github.io/bondi/"
|
|
27
|
+
Repository = "https://github.com/relaxed-constraints/bondi"
|
|
28
|
+
Issues = "https://github.com/relaxed-constraints/bondi/issues"
|
|
29
|
+
Changelog = "https://github.com/relaxed-constraints/bondi/blob/main/CHANGELOG.md"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
couchbase = ["couchbase>=4.6.3,<5"]
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling>=1.26"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = ["pytest>=8", "ruff>=0.9"]
|
|
40
|
+
docs = [
|
|
41
|
+
"mkdocs>=1.6,<2",
|
|
42
|
+
"mkdocs-material>=9.6,<10",
|
|
43
|
+
"mkdocstrings[python]>=0.28",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/bondi"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests", "src/bondi"]
|
|
51
|
+
addopts = "--import-mode=importlib --doctest-modules"
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 100
|
|
55
|
+
target-version = "py311"
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = ["E", "F", "I", "UP"]
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.targets.sdist]
|
|
61
|
+
include = ["/src", "/tests", "/pyproject.toml", "/uv.lock", "/README.md", "/LICENSE", "/CHANGELOG.md", "/CITATION.cff"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""A Python toolkit for document database research."""
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Private background-loop bridge for synchronous database facades."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from concurrent.futures import Future
|
|
5
|
+
from threading import Event, Thread, get_ident
|
|
6
|
+
from typing import Self
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _SyncExecutor:
|
|
10
|
+
"""Own an async executor on a background loop; calls stay on the owner thread."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
raise TypeError("Use from_uri() to create an owned executor")
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def _create(cls, factory):
|
|
17
|
+
self = object.__new__(cls)
|
|
18
|
+
self._thread = get_ident()
|
|
19
|
+
self._closed = False
|
|
20
|
+
self._executor = None
|
|
21
|
+
ready = Event()
|
|
22
|
+
|
|
23
|
+
def serve():
|
|
24
|
+
with asyncio.Runner() as runner:
|
|
25
|
+
self._loop = runner.get_loop()
|
|
26
|
+
ready.set()
|
|
27
|
+
self._loop.run_forever()
|
|
28
|
+
|
|
29
|
+
self._worker = Thread(target=serve, name=f"bondi-{cls.__name__}", daemon=True)
|
|
30
|
+
self._worker.start()
|
|
31
|
+
ready.wait()
|
|
32
|
+
|
|
33
|
+
async def connect():
|
|
34
|
+
self._executor = await factory()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
self._submit(connect)
|
|
38
|
+
except BaseException:
|
|
39
|
+
try:
|
|
40
|
+
if self._executor is not None:
|
|
41
|
+
self._submit(self._executor.close, cancel_on_interrupt=False)
|
|
42
|
+
finally:
|
|
43
|
+
self._shutdown()
|
|
44
|
+
raise
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def _check(self):
|
|
48
|
+
if get_ident() != self._thread:
|
|
49
|
+
raise RuntimeError("Use this executor only from its creating thread")
|
|
50
|
+
if self._closed:
|
|
51
|
+
raise RuntimeError("Executor is closed")
|
|
52
|
+
|
|
53
|
+
def _submit(self, operation, *, cancel_on_interrupt=True):
|
|
54
|
+
"""Wait for an operation, including its cancellation cleanup on interrupt."""
|
|
55
|
+
self._check()
|
|
56
|
+
outcome = Future()
|
|
57
|
+
task = None
|
|
58
|
+
|
|
59
|
+
async def invoke():
|
|
60
|
+
try:
|
|
61
|
+
value = await operation()
|
|
62
|
+
except BaseException as error:
|
|
63
|
+
outcome.set_exception(error)
|
|
64
|
+
else:
|
|
65
|
+
outcome.set_result(value)
|
|
66
|
+
|
|
67
|
+
def start():
|
|
68
|
+
nonlocal task
|
|
69
|
+
task = self._loop.create_task(invoke())
|
|
70
|
+
|
|
71
|
+
def cancel():
|
|
72
|
+
if task is not None and not task.done():
|
|
73
|
+
# Schedule after the already-queued first task step.
|
|
74
|
+
self._loop.call_soon(task.cancel)
|
|
75
|
+
|
|
76
|
+
self._loop.call_soon_threadsafe(start)
|
|
77
|
+
try:
|
|
78
|
+
return outcome.result()
|
|
79
|
+
except KeyboardInterrupt:
|
|
80
|
+
if cancel_on_interrupt:
|
|
81
|
+
self._loop.call_soon_threadsafe(cancel)
|
|
82
|
+
# Do not return control until the coroutine has finished unwinding.
|
|
83
|
+
while not outcome.done():
|
|
84
|
+
try:
|
|
85
|
+
outcome.result(timeout=0.1)
|
|
86
|
+
except (KeyboardInterrupt, TimeoutError):
|
|
87
|
+
continue
|
|
88
|
+
except BaseException:
|
|
89
|
+
break
|
|
90
|
+
raise
|
|
91
|
+
|
|
92
|
+
def _call(self, method, *args, **kwargs):
|
|
93
|
+
async def invoke():
|
|
94
|
+
return await getattr(self._executor, method)(*args, **kwargs)
|
|
95
|
+
|
|
96
|
+
return self._submit(invoke)
|
|
97
|
+
|
|
98
|
+
def _shutdown(self):
|
|
99
|
+
self._closed = True
|
|
100
|
+
self._loop.call_soon_threadsafe(self._loop.stop)
|
|
101
|
+
while self._worker.is_alive():
|
|
102
|
+
try:
|
|
103
|
+
self._worker.join(timeout=0.1)
|
|
104
|
+
except KeyboardInterrupt:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
def close(self) -> None:
|
|
108
|
+
"""Close the connection, stop its loop and join its thread; idempotent."""
|
|
109
|
+
if self._closed:
|
|
110
|
+
return
|
|
111
|
+
self._check()
|
|
112
|
+
try:
|
|
113
|
+
self._submit(self._executor.close, cancel_on_interrupt=False)
|
|
114
|
+
finally:
|
|
115
|
+
self._shutdown()
|
|
116
|
+
|
|
117
|
+
def __enter__(self) -> Self:
|
|
118
|
+
self._check()
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
def __exit__(self, exc_type, exc, traceback) -> None:
|
|
122
|
+
self.close()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Native Couchbase query descriptions without an SDK connection."""
|
|
2
|
+
|
|
3
|
+
from bondi.couchbase.executor import AsyncCouchbaseExecutor
|
|
4
|
+
from bondi.couchbase.explain import summarize
|
|
5
|
+
from bondi.couchbase.query import CouchbaseQuery
|
|
6
|
+
from bondi.couchbase.sync_executor import CouchbaseExecutor
|
|
7
|
+
|
|
8
|
+
__all__ = ["CouchbaseExecutor", "CouchbaseQuery", "AsyncCouchbaseExecutor", "summarize"]
|