python-datastore-sqlalchemy 0.0.1__tar.gz → 0.0.3__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.
- python_datastore_sqlalchemy-0.0.3/PKG-INFO +95 -0
- python_datastore_sqlalchemy-0.0.3/README.md +65 -0
- python_datastore_sqlalchemy-0.0.3/pyproject.toml +88 -0
- python_datastore_sqlalchemy-0.0.3/python_datastore_sqlalchemy.egg-info/PKG-INFO +95 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/python_datastore_sqlalchemy.egg-info/SOURCES.txt +0 -1
- python_datastore_sqlalchemy-0.0.3/python_datastore_sqlalchemy.egg-info/requires.txt +8 -0
- python_datastore_sqlalchemy-0.0.3/setup.py +22 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/__init__.py +2 -2
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/_helpers.py +11 -9
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/base.py +28 -60
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/datastore_dbapi.py +20 -5
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/parse_url.py +8 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_base.py +79 -1
- python_datastore_sqlalchemy-0.0.1/PKG-INFO +0 -65
- python_datastore_sqlalchemy-0.0.1/README.md +0 -28
- python_datastore_sqlalchemy-0.0.1/pyproject.toml +0 -21
- python_datastore_sqlalchemy-0.0.1/python_datastore_sqlalchemy.egg-info/PKG-INFO +0 -65
- python_datastore_sqlalchemy-0.0.1/python_datastore_sqlalchemy.egg-info/not-zip-safe +0 -1
- python_datastore_sqlalchemy-0.0.1/python_datastore_sqlalchemy.egg-info/requires.txt +0 -1
- python_datastore_sqlalchemy-0.0.1/setup.py +0 -70
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/LICENSE +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/python_datastore_sqlalchemy.egg-info/dependency_links.txt +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/python_datastore_sqlalchemy.egg-info/entry_points.txt +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/python_datastore_sqlalchemy.egg-info/top_level.txt +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/setup.cfg +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/_types.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_datastore.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_gql.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_integration_queries.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_orm.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_dbapi.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_helpers.py +0 -0
- {python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_types.py +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-datastore-sqlalchemy
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: SQLAlchemy dialect for google cloud datastore
|
|
5
|
+
Author-email: "HY Chang(splasky)" <hychang.1997.tw@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/splasky/python-datastore-sqlalchemy/wiki
|
|
8
|
+
Project-URL: Source, https://github.com/splasky/python-datastore-sqlalchemy
|
|
9
|
+
Project-URL: Tracker, https://github.com/splasky/python-datastore-sqlalchemy/issues
|
|
10
|
+
Keywords: SQLAlchemy,GCP,Datastore
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Classifier: Topic :: Database :: Front-Ends
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: sqlalchemy<3.0.0,>=1.4.16
|
|
22
|
+
Requires-Dist: google-cloud-datastore>=2.21.0
|
|
23
|
+
Requires-Dist: google-cloud-firestore>=2.21.0
|
|
24
|
+
Requires-Dist: google-cloud-bigquery>=3.35.0
|
|
25
|
+
Requires-Dist: google-auth>=2.40.0
|
|
26
|
+
Requires-Dist: sqlglot>=28.0.0
|
|
27
|
+
Requires-Dist: pandas>=2.0.0
|
|
28
|
+
Requires-Dist: requests
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
SQLAlchemy dialect for Google Cloud Datastore in Firestore mode
|
|
32
|
+
========================
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install from PyPI:
|
|
37
|
+
```bash
|
|
38
|
+
pip install python-datastore-sqlalchemy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Install from a local checkout:
|
|
42
|
+
```bash
|
|
43
|
+
uv pip install -e .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Runtime dependencies are declared in `pyproject.toml`. `requirements.txt` is not used by this project.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from sqlalchemy import create_engine, text
|
|
52
|
+
|
|
53
|
+
engine = create_engine(
|
|
54
|
+
"datastore://my-project/?database=my-db",
|
|
55
|
+
credentials="path/to/credentials.json",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
with engine.connect() as conn:
|
|
59
|
+
result = conn.execute(text("SELECT * FROM test_table"))
|
|
60
|
+
print(result.fetchall())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Preview
|
|
64
|
+
|
|
65
|
+
<img src="assets/pie.png">
|
|
66
|
+
|
|
67
|
+
> [!WARNING]
|
|
68
|
+
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
69
|
+
|
|
70
|
+
## How to contribute
|
|
71
|
+
|
|
72
|
+
Feel free to open issues and pull requests on GitHub.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
This project supports Python 3.10 and newer. It uses a uv-managed virtual environment by default. The lockfile is tracked so local development and CI use the same dependency resolution.
|
|
77
|
+
|
|
78
|
+
Install the development environment:
|
|
79
|
+
```bash
|
|
80
|
+
uv sync --locked
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run the test suite:
|
|
84
|
+
```bash
|
|
85
|
+
uv run pytest
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The Datastore-backed tests require the Google Cloud SDK with the `beta` and `cloud-datastore-emulator` components installed. If `gcloud` is not on `PATH`, point the test fixture at it explicitly:
|
|
89
|
+
```bash
|
|
90
|
+
GCLOUD_PATH=/path/to/google-cloud-sdk/bin/gcloud uv run pytest
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Development Notes
|
|
94
|
+
|
|
95
|
+
- [Develop a SQLAlchemy and its dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
SQLAlchemy dialect for Google Cloud Datastore in Firestore mode
|
|
2
|
+
========================
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
|
|
6
|
+
Install from PyPI:
|
|
7
|
+
```bash
|
|
8
|
+
pip install python-datastore-sqlalchemy
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Install from a local checkout:
|
|
12
|
+
```bash
|
|
13
|
+
uv pip install -e .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Runtime dependencies are declared in `pyproject.toml`. `requirements.txt` is not used by this project.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from sqlalchemy import create_engine, text
|
|
22
|
+
|
|
23
|
+
engine = create_engine(
|
|
24
|
+
"datastore://my-project/?database=my-db",
|
|
25
|
+
credentials="path/to/credentials.json",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
with engine.connect() as conn:
|
|
29
|
+
result = conn.execute(text("SELECT * FROM test_table"))
|
|
30
|
+
print(result.fetchall())
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Preview
|
|
34
|
+
|
|
35
|
+
<img src="assets/pie.png">
|
|
36
|
+
|
|
37
|
+
> [!WARNING]
|
|
38
|
+
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
39
|
+
|
|
40
|
+
## How to contribute
|
|
41
|
+
|
|
42
|
+
Feel free to open issues and pull requests on GitHub.
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
This project supports Python 3.10 and newer. It uses a uv-managed virtual environment by default. The lockfile is tracked so local development and CI use the same dependency resolution.
|
|
47
|
+
|
|
48
|
+
Install the development environment:
|
|
49
|
+
```bash
|
|
50
|
+
uv sync --locked
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run the test suite:
|
|
54
|
+
```bash
|
|
55
|
+
uv run pytest
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The Datastore-backed tests require the Google Cloud SDK with the `beta` and `cloud-datastore-emulator` components installed. If `gcloud` is not on `PATH`, point the test fixture at it explicitly:
|
|
59
|
+
```bash
|
|
60
|
+
GCLOUD_PATH=/path/to/google-cloud-sdk/bin/gcloud uv run pytest
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Development Notes
|
|
64
|
+
|
|
65
|
+
- [Develop a SQLAlchemy and its dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-datastore-sqlalchemy"
|
|
7
|
+
version = "0.0.3"
|
|
8
|
+
description = "SQLAlchemy dialect for google cloud datastore"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "HY Chang(splasky)", email = "hychang.1997.tw@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
21
|
+
"Topic :: Database :: Front-Ends",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
]
|
|
24
|
+
keywords = ["SQLAlchemy", "GCP", "Datastore"]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"sqlalchemy>=1.4.16,<3.0.0",
|
|
27
|
+
"google-cloud-datastore>=2.21.0",
|
|
28
|
+
"google-cloud-firestore>=2.21.0",
|
|
29
|
+
"google-cloud-bigquery>=3.35.0",
|
|
30
|
+
"google-auth>=2.40.0",
|
|
31
|
+
"sqlglot>=28.0.0",
|
|
32
|
+
"pandas>=2.0.0",
|
|
33
|
+
"requests",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.entry-points."sqlalchemy.dialects"]
|
|
37
|
+
datastore = "sqlalchemy_datastore:CloudDatastoreDialect"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Documentation = "https://github.com/splasky/python-datastore-sqlalchemy/wiki"
|
|
41
|
+
Source = "https://github.com/splasky/python-datastore-sqlalchemy"
|
|
42
|
+
Tracker = "https://github.com/splasky/python-datastore-sqlalchemy/issues"
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = [
|
|
46
|
+
"coverage==7.9.2",
|
|
47
|
+
"flake8==7.3.0",
|
|
48
|
+
"mypy",
|
|
49
|
+
"pytest==9.0.3",
|
|
50
|
+
"pytest-cov==6.2.1",
|
|
51
|
+
"pytest-html==4.1.1",
|
|
52
|
+
"pytest-mypy==1.0.1",
|
|
53
|
+
"pytest-ruff==0.5",
|
|
54
|
+
"ruff",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
[tool.setuptools.packages.find]
|
|
60
|
+
include = ["sqlalchemy_datastore*"]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
addopts = "--ruff --mypy --cov --cov-report=html"
|
|
64
|
+
pythonpath = ["."]
|
|
65
|
+
testpaths = ["tests"]
|
|
66
|
+
python_files = ["test_*.py"]
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 120
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint]
|
|
72
|
+
select = ["E", "F", "W", "I", "N", "B", "C", "G", "S"]
|
|
73
|
+
ignore = [
|
|
74
|
+
"E501", # Line too long - allow longer lines for SQL strings in tests
|
|
75
|
+
"S608", # SQL injection - intentional for test cases
|
|
76
|
+
"B007", # Loop variable not used - common in test assertions
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint.per-file-ignores]
|
|
80
|
+
"tests/*" = ["S608", "E501", "S101", "B007", "S603"]
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
ignore_missing_imports = true
|
|
84
|
+
disable_error_code = ["var-annotated", "import-untyped"]
|
|
85
|
+
|
|
86
|
+
[[tool.mypy.overrides]]
|
|
87
|
+
module = ["tests.*"]
|
|
88
|
+
ignore_errors = true
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-datastore-sqlalchemy
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: SQLAlchemy dialect for google cloud datastore
|
|
5
|
+
Author-email: "HY Chang(splasky)" <hychang.1997.tw@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/splasky/python-datastore-sqlalchemy/wiki
|
|
8
|
+
Project-URL: Source, https://github.com/splasky/python-datastore-sqlalchemy
|
|
9
|
+
Project-URL: Tracker, https://github.com/splasky/python-datastore-sqlalchemy/issues
|
|
10
|
+
Keywords: SQLAlchemy,GCP,Datastore
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Classifier: Topic :: Database :: Front-Ends
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: sqlalchemy<3.0.0,>=1.4.16
|
|
22
|
+
Requires-Dist: google-cloud-datastore>=2.21.0
|
|
23
|
+
Requires-Dist: google-cloud-firestore>=2.21.0
|
|
24
|
+
Requires-Dist: google-cloud-bigquery>=3.35.0
|
|
25
|
+
Requires-Dist: google-auth>=2.40.0
|
|
26
|
+
Requires-Dist: sqlglot>=28.0.0
|
|
27
|
+
Requires-Dist: pandas>=2.0.0
|
|
28
|
+
Requires-Dist: requests
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
SQLAlchemy dialect for Google Cloud Datastore in Firestore mode
|
|
32
|
+
========================
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install from PyPI:
|
|
37
|
+
```bash
|
|
38
|
+
pip install python-datastore-sqlalchemy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Install from a local checkout:
|
|
42
|
+
```bash
|
|
43
|
+
uv pip install -e .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Runtime dependencies are declared in `pyproject.toml`. `requirements.txt` is not used by this project.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from sqlalchemy import create_engine, text
|
|
52
|
+
|
|
53
|
+
engine = create_engine(
|
|
54
|
+
"datastore://my-project/?database=my-db",
|
|
55
|
+
credentials="path/to/credentials.json",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
with engine.connect() as conn:
|
|
59
|
+
result = conn.execute(text("SELECT * FROM test_table"))
|
|
60
|
+
print(result.fetchall())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Preview
|
|
64
|
+
|
|
65
|
+
<img src="assets/pie.png">
|
|
66
|
+
|
|
67
|
+
> [!WARNING]
|
|
68
|
+
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
69
|
+
|
|
70
|
+
## How to contribute
|
|
71
|
+
|
|
72
|
+
Feel free to open issues and pull requests on GitHub.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
This project supports Python 3.10 and newer. It uses a uv-managed virtual environment by default. The lockfile is tracked so local development and CI use the same dependency resolution.
|
|
77
|
+
|
|
78
|
+
Install the development environment:
|
|
79
|
+
```bash
|
|
80
|
+
uv sync --locked
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run the test suite:
|
|
84
|
+
```bash
|
|
85
|
+
uv run pytest
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The Datastore-backed tests require the Google Cloud SDK with the `beta` and `cloud-datastore-emulator` components installed. If `gcloud` is not on `PATH`, point the test fixture at it explicitly:
|
|
89
|
+
```bash
|
|
90
|
+
GCLOUD_PATH=/path/to/google-cloud-sdk/bin/gcloud uv run pytest
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Development Notes
|
|
94
|
+
|
|
95
|
+
- [Develop a SQLAlchemy and its dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -6,7 +6,6 @@ python_datastore_sqlalchemy.egg-info/PKG-INFO
|
|
|
6
6
|
python_datastore_sqlalchemy.egg-info/SOURCES.txt
|
|
7
7
|
python_datastore_sqlalchemy.egg-info/dependency_links.txt
|
|
8
8
|
python_datastore_sqlalchemy.egg-info/entry_points.txt
|
|
9
|
-
python_datastore_sqlalchemy.egg-info/not-zip-safe
|
|
10
9
|
python_datastore_sqlalchemy.egg-info/requires.txt
|
|
11
10
|
python_datastore_sqlalchemy.egg-info/top_level.txt
|
|
12
11
|
sqlalchemy_datastore/__init__.py
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright (c) 2025 hychang <hychang.1997.tw@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
# this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
# the Software without restriction, including without limitation the rights to
|
|
6
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
# subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
# copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
+
|
|
20
|
+
from setuptools import setup
|
|
21
|
+
|
|
22
|
+
setup()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2025 hychang <hychang.1997.tw@gmail.com>
|
|
1
|
+
# Copyright (c) 2025 hychang <hychang.1997.tw@gmail.com>
|
|
2
2
|
#
|
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
4
|
# this software and associated documentation files (the "Software"), to deal in
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
from .base import CloudDatastoreDialect
|
|
21
21
|
|
|
22
|
-
__version__ = "0.0.
|
|
22
|
+
__version__ = "0.0.3"
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
25
|
"__version__",
|
|
@@ -60,21 +60,23 @@ def create_datastore_client(
|
|
|
60
60
|
user_agent: Optional[client_info.ClientInfo] = None,
|
|
61
61
|
database: Optional[str] = None
|
|
62
62
|
) -> Tuple[datastore.Client, Optional[Credentials]]:
|
|
63
|
-
"""Construct a
|
|
63
|
+
"""Construct a Datastore client object.
|
|
64
64
|
|
|
65
65
|
Args:
|
|
66
|
-
credentials_info Optional[dict]:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
credentials_info (Optional[dict]):
|
|
67
|
+
Service account info dictionary.
|
|
68
|
+
credentials_path (Optional[str]):
|
|
69
|
+
Path to a service account JSON key file.
|
|
70
|
+
credentials_base64 (Optional[str]):
|
|
71
|
+
Base64-encoded service account JSON key.
|
|
71
72
|
project_id (Optional[str]):
|
|
72
73
|
Project ID for the project which the client acts on behalf of.
|
|
73
74
|
user_agent (Optional[google.api_core.client_info.ClientInfo]):
|
|
74
75
|
The client info used to send a user-agent string along with API
|
|
75
|
-
requests. If ``None``, then default info will be used.
|
|
76
|
-
|
|
77
|
-
or
|
|
76
|
+
requests. If ``None``, then default info will be used.
|
|
77
|
+
database (Optional[str]):
|
|
78
|
+
Datastore database ID. ``None`` or ``"(default)"`` uses the
|
|
79
|
+
default database.
|
|
78
80
|
"""
|
|
79
81
|
|
|
80
82
|
default_project = None
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/sqlalchemy_datastore/base.py
RENAMED
|
@@ -17,20 +17,15 @@
|
|
|
17
17
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
18
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
19
|
import logging
|
|
20
|
-
from concurrent import futures
|
|
21
20
|
from typing import Any, List, Optional
|
|
22
21
|
|
|
23
22
|
from google.cloud import firestore_admin_v1
|
|
24
|
-
from google.cloud.firestore_admin_v1.types import Database
|
|
25
23
|
from google.oauth2 import service_account
|
|
26
24
|
from sqlalchemy import exc
|
|
27
25
|
from sqlalchemy.engine import Connection, default
|
|
28
26
|
from sqlalchemy.engine.interfaces import (
|
|
29
|
-
# DBAPICursor,
|
|
30
|
-
# _DBAPISingleExecuteParams,
|
|
31
27
|
ExecutionContext,
|
|
32
28
|
)
|
|
33
|
-
from sqlalchemy.sql import Select
|
|
34
29
|
|
|
35
30
|
from . import _types, datastore_dbapi
|
|
36
31
|
from ._helpers import create_datastore_client
|
|
@@ -88,6 +83,7 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
88
83
|
self.location = location
|
|
89
84
|
self.identifier_preparer = self.preparer(self)
|
|
90
85
|
self.dataset_id = None
|
|
86
|
+
self.database_id = None
|
|
91
87
|
self.list_tables_page_size = list_tables_page_size
|
|
92
88
|
self._client = None
|
|
93
89
|
|
|
@@ -129,6 +125,7 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
129
125
|
provided_job_config,
|
|
130
126
|
list_tables_page_size,
|
|
131
127
|
user_supplied_client,
|
|
128
|
+
database_id,
|
|
132
129
|
) = parse_url(url)
|
|
133
130
|
|
|
134
131
|
self.arraysize = arraysize or self.arraysize
|
|
@@ -138,6 +135,7 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
138
135
|
self.credentials_path = credentials_path
|
|
139
136
|
self.credentials_base64 = credentials_base64 or self.credentials_base64
|
|
140
137
|
self.dataset_id = dataset_id
|
|
138
|
+
self.database_id = database_id
|
|
141
139
|
self.billing_project_id = self.billing_project_id or self.project_id
|
|
142
140
|
|
|
143
141
|
if user_supplied_client:
|
|
@@ -148,7 +146,7 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
148
146
|
credentials_info=self.credentials_info,
|
|
149
147
|
credentials_base64=self.credentials_base64,
|
|
150
148
|
project_id=self.billing_project_id,
|
|
151
|
-
database=
|
|
149
|
+
database=self.database_id,
|
|
152
150
|
)
|
|
153
151
|
self.project_id = self.project_id if self.project_id else client.project
|
|
154
152
|
self.billing_project_id = (
|
|
@@ -162,10 +160,10 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
162
160
|
|
|
163
161
|
self._client = client
|
|
164
162
|
self.credentials = credentials
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
163
|
+
self._client.credentials_path = self.credentials_path
|
|
164
|
+
self._client.credentials_info = self.credentials_info
|
|
165
|
+
self._client.credentials_base64 = self.credentials_base64
|
|
166
|
+
self._client.scoped_credentials = credentials
|
|
169
167
|
return ([], {"client": client})
|
|
170
168
|
|
|
171
169
|
def get_schema_names(self, connection: Connection, **kw) -> List[str]:
|
|
@@ -182,18 +180,11 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
182
180
|
|
|
183
181
|
try:
|
|
184
182
|
list_database_resp = client.list_databases(parent=parent)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if full_name
|
|
189
|
-
|
|
190
|
-
return None
|
|
191
|
-
|
|
192
|
-
with futures.ThreadPoolExecutor() as executor:
|
|
193
|
-
schemas = list(
|
|
194
|
-
executor.map(get_database_short_name, list_database_resp.databases)
|
|
195
|
-
)
|
|
196
|
-
|
|
183
|
+
schemas = [
|
|
184
|
+
full_name.split("/")[-1]
|
|
185
|
+
for db in list_database_resp.databases
|
|
186
|
+
if (full_name := db.name)
|
|
187
|
+
]
|
|
197
188
|
return schemas
|
|
198
189
|
except Exception as e:
|
|
199
190
|
logging.error(e)
|
|
@@ -206,57 +197,34 @@ class CloudDatastoreDialect(default.DefaultDialect):
|
|
|
206
197
|
query = client.query(kind="__kind__")
|
|
207
198
|
kinds = list(query.fetch())
|
|
208
199
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
with futures.ThreadPoolExecutor() as executor:
|
|
220
|
-
result = list(executor.map(get_kind_name, kinds))
|
|
221
|
-
|
|
222
|
-
return [t for t in result if t is not None]
|
|
200
|
+
return [
|
|
201
|
+
name
|
|
202
|
+
for kind in kinds
|
|
203
|
+
if (name := getattr(getattr(kind, "key", None), "name", None))
|
|
204
|
+
is not None
|
|
205
|
+
and isinstance(name, str)
|
|
206
|
+
and not name.startswith("__")
|
|
207
|
+
]
|
|
223
208
|
|
|
224
209
|
def get_columns(
|
|
225
210
|
self, connection: Connection, table_name: str, schema: str | None = None, **kw
|
|
226
211
|
):
|
|
227
|
-
"""Retrieve column information from the database
|
|
212
|
+
"""Retrieve column information from the database."""
|
|
228
213
|
client = self._client
|
|
229
214
|
query = client.query(kind="__Stat_PropertyType_PropertyName_Kind__")
|
|
230
215
|
query.add_filter("kind_name", "=", table_name)
|
|
231
216
|
properties = list(query.fetch())
|
|
232
217
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"name":
|
|
236
|
-
"type": _types._property_type[
|
|
218
|
+
return [
|
|
219
|
+
{
|
|
220
|
+
"name": prop["property_name"],
|
|
221
|
+
"type": _types._property_type[prop["property_type"]],
|
|
237
222
|
"nullable": True,
|
|
238
223
|
"comment": "",
|
|
239
224
|
"default": None,
|
|
240
225
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
columns = list(executor.map(process_property, properties))
|
|
244
|
-
return columns
|
|
245
|
-
|
|
246
|
-
def _contains_select_subquery(self, node) -> bool:
|
|
247
|
-
"""
|
|
248
|
-
Check the AST node contains select subquery
|
|
249
|
-
"""
|
|
250
|
-
if isinstance(node, Select):
|
|
251
|
-
for child in node.get_children():
|
|
252
|
-
if isinstance(child, Select) or self._contains_select_subquery(child):
|
|
253
|
-
return True
|
|
254
|
-
|
|
255
|
-
for child in node.get_children():
|
|
256
|
-
if isinstance(child, Select) or self._contains_select_subquery(child):
|
|
257
|
-
return True
|
|
258
|
-
|
|
259
|
-
return False
|
|
226
|
+
for prop in properties
|
|
227
|
+
]
|
|
260
228
|
|
|
261
229
|
def do_execute(
|
|
262
230
|
self,
|
|
@@ -228,7 +228,7 @@ class Cursor:
|
|
|
228
228
|
|
|
229
229
|
except Exception as e:
|
|
230
230
|
logging.error(f"INSERT failed: {e}")
|
|
231
|
-
raise ProgrammingError(f"INSERT failed: {e}")
|
|
231
|
+
raise ProgrammingError(f"INSERT failed: {e}") from e
|
|
232
232
|
|
|
233
233
|
def _execute_update(self, statement: str, parameters=None):
|
|
234
234
|
"""Execute an UPDATE statement using Datastore client."""
|
|
@@ -454,6 +454,8 @@ class Cursor:
|
|
|
454
454
|
r"\bCOUNT_UP_TO\s*\(",
|
|
455
455
|
r"\bSUM\s*\(",
|
|
456
456
|
r"\bAVG\s*\(",
|
|
457
|
+
r"\bMIN\s*\(",
|
|
458
|
+
r"\bMAX\s*\(",
|
|
457
459
|
]
|
|
458
460
|
for pattern in agg_patterns:
|
|
459
461
|
if re.search(pattern, upper):
|
|
@@ -931,9 +933,14 @@ class Cursor:
|
|
|
931
933
|
# This is a simplified evaluator for common patterns
|
|
932
934
|
try:
|
|
933
935
|
return self._eval_condition(context, where_clause)
|
|
934
|
-
except Exception:
|
|
935
|
-
|
|
936
|
-
|
|
936
|
+
except Exception as e:
|
|
937
|
+
logging.warning(
|
|
938
|
+
"Client-side WHERE evaluation failed for clause '%s': %s. "
|
|
939
|
+
"Row will be excluded (fail closed).",
|
|
940
|
+
where_clause,
|
|
941
|
+
e,
|
|
942
|
+
)
|
|
943
|
+
return False
|
|
937
944
|
|
|
938
945
|
def _eval_condition(self, context: Dict[str, Any], condition: str) -> bool:
|
|
939
946
|
"""Evaluate a single condition or compound condition."""
|
|
@@ -1566,10 +1573,12 @@ class Cursor:
|
|
|
1566
1573
|
self.description = list(agg_fields.values())
|
|
1567
1574
|
|
|
1568
1575
|
def execute_orm(
|
|
1569
|
-
self, statement: str, parameters=None, tokens: List[tokens.Token] =
|
|
1576
|
+
self, statement: str, parameters=None, tokens: Optional[List[tokens.Token]] = None
|
|
1570
1577
|
):
|
|
1571
1578
|
if parameters is None:
|
|
1572
1579
|
parameters = {}
|
|
1580
|
+
if tokens is None:
|
|
1581
|
+
tokens = []
|
|
1573
1582
|
|
|
1574
1583
|
logging.debug(
|
|
1575
1584
|
f"[DataStore DBAPI] Executing ORM query: {statement} with parameters: {parameters}"
|
|
@@ -1812,11 +1821,15 @@ class Cursor:
|
|
|
1812
1821
|
def fetchall(self):
|
|
1813
1822
|
if self._closed:
|
|
1814
1823
|
raise Error("Cursor is closed.")
|
|
1824
|
+
if self._query_rows is None:
|
|
1825
|
+
raise ProgrammingError("No query has been executed.")
|
|
1815
1826
|
return list(self._query_rows)
|
|
1816
1827
|
|
|
1817
1828
|
def fetchmany(self, size=None):
|
|
1818
1829
|
if self._closed:
|
|
1819
1830
|
raise Error("Cursor is closed.")
|
|
1831
|
+
if self._query_rows is None:
|
|
1832
|
+
raise ProgrammingError("No query has been executed.")
|
|
1820
1833
|
if size is None:
|
|
1821
1834
|
size = self.arraysize or 1
|
|
1822
1835
|
results = []
|
|
@@ -1830,6 +1843,8 @@ class Cursor:
|
|
|
1830
1843
|
def fetchone(self):
|
|
1831
1844
|
if self._closed:
|
|
1832
1845
|
raise Error("Cursor is closed.")
|
|
1846
|
+
if self._query_rows is None:
|
|
1847
|
+
raise ProgrammingError("No query has been executed.")
|
|
1833
1848
|
try:
|
|
1834
1849
|
return next(self._query_rows)
|
|
1835
1850
|
except StopIteration:
|
|
@@ -72,6 +72,11 @@ def parse_url(url): # noqa: C901
|
|
|
72
72
|
credentials_base64 = None
|
|
73
73
|
list_tables_page_size = None
|
|
74
74
|
user_supplied_client = False
|
|
75
|
+
database = None
|
|
76
|
+
|
|
77
|
+
# database
|
|
78
|
+
if "database" in query:
|
|
79
|
+
database = query.pop("database")
|
|
75
80
|
|
|
76
81
|
# location
|
|
77
82
|
if "location" in query:
|
|
@@ -122,6 +127,7 @@ def parse_url(url): # noqa: C901
|
|
|
122
127
|
QueryJobConfig(),
|
|
123
128
|
list_tables_page_size,
|
|
124
129
|
user_supplied_client,
|
|
130
|
+
database,
|
|
125
131
|
)
|
|
126
132
|
else:
|
|
127
133
|
return (
|
|
@@ -134,6 +140,7 @@ def parse_url(url): # noqa: C901
|
|
|
134
140
|
None,
|
|
135
141
|
list_tables_page_size,
|
|
136
142
|
user_supplied_client,
|
|
143
|
+
database,
|
|
137
144
|
)
|
|
138
145
|
|
|
139
146
|
job_config = QueryJobConfig()
|
|
@@ -284,4 +291,5 @@ def parse_url(url): # noqa: C901
|
|
|
284
291
|
job_config,
|
|
285
292
|
list_tables_page_size,
|
|
286
293
|
user_supplied_client,
|
|
294
|
+
database,
|
|
287
295
|
)
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_base.py
RENAMED
|
@@ -17,9 +17,12 @@
|
|
|
17
17
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
18
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
19
|
"""Unit tests for base.py CloudDatastoreDialect (no emulator required)."""
|
|
20
|
-
from unittest.mock import MagicMock
|
|
20
|
+
from unittest.mock import MagicMock, patch
|
|
21
|
+
|
|
22
|
+
from sqlalchemy.engine.url import make_url
|
|
21
23
|
|
|
22
24
|
from sqlalchemy_datastore import CloudDatastoreDialect, datastore_dbapi
|
|
25
|
+
from sqlalchemy_datastore.parse_url import parse_url
|
|
23
26
|
|
|
24
27
|
# ---------------------------------------------------------------------------
|
|
25
28
|
# Dialect metadata
|
|
@@ -224,3 +227,78 @@ def test_get_table_names_empty():
|
|
|
224
227
|
|
|
225
228
|
result = d.get_table_names(None)
|
|
226
229
|
assert result == []
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
# parse_url – database parameter
|
|
234
|
+
# ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
def test_parse_url_extracts_database():
|
|
237
|
+
url = make_url("datastore://my-project/?database=my-db")
|
|
238
|
+
result = parse_url(url)
|
|
239
|
+
assert len(result) == 10
|
|
240
|
+
assert result[9] == "my-db"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_parse_url_no_database():
|
|
244
|
+
url = make_url("datastore://my-project/")
|
|
245
|
+
result = parse_url(url)
|
|
246
|
+
assert len(result) == 10
|
|
247
|
+
assert result[9] is None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def test_parse_url_database_with_location():
|
|
251
|
+
url = make_url("datastore://my-project/?database=my-db&location=us-east1")
|
|
252
|
+
result = parse_url(url)
|
|
253
|
+
assert result[9] == "my-db"
|
|
254
|
+
assert result[1] == "us-east1"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# ---------------------------------------------------------------------------
|
|
258
|
+
# create_connect_args – database parameter
|
|
259
|
+
# ---------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
def test_create_connect_args_passes_database():
|
|
262
|
+
d = CloudDatastoreDialect()
|
|
263
|
+
url = make_url("datastore://my-project/?database=my-db")
|
|
264
|
+
|
|
265
|
+
with patch(
|
|
266
|
+
"sqlalchemy_datastore.base.create_datastore_client"
|
|
267
|
+
) as mock_create:
|
|
268
|
+
mock_client = MagicMock()
|
|
269
|
+
mock_client.project = "my-project"
|
|
270
|
+
mock_create.return_value = (mock_client, MagicMock())
|
|
271
|
+
|
|
272
|
+
d.create_connect_args(url)
|
|
273
|
+
|
|
274
|
+
mock_create.assert_called_once_with(
|
|
275
|
+
credentials_path=None,
|
|
276
|
+
credentials_info=None,
|
|
277
|
+
credentials_base64=None,
|
|
278
|
+
project_id="my-project",
|
|
279
|
+
database="my-db",
|
|
280
|
+
)
|
|
281
|
+
assert d.database_id == "my-db"
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_create_connect_args_no_database():
|
|
285
|
+
d = CloudDatastoreDialect()
|
|
286
|
+
url = make_url("datastore://my-project/")
|
|
287
|
+
|
|
288
|
+
with patch(
|
|
289
|
+
"sqlalchemy_datastore.base.create_datastore_client"
|
|
290
|
+
) as mock_create:
|
|
291
|
+
mock_client = MagicMock()
|
|
292
|
+
mock_client.project = "my-project"
|
|
293
|
+
mock_create.return_value = (mock_client, MagicMock())
|
|
294
|
+
|
|
295
|
+
d.create_connect_args(url)
|
|
296
|
+
|
|
297
|
+
mock_create.assert_called_once_with(
|
|
298
|
+
credentials_path=None,
|
|
299
|
+
credentials_info=None,
|
|
300
|
+
credentials_base64=None,
|
|
301
|
+
project_id="my-project",
|
|
302
|
+
database=None,
|
|
303
|
+
)
|
|
304
|
+
assert d.database_id is None
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python-datastore-sqlalchemy
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: SQLAlchemy dialect for google cloud datastore
|
|
5
|
-
Home-page: https://github.com/splasky/python-datastore-sqlalchemy
|
|
6
|
-
Author: HY Chang(splasky)
|
|
7
|
-
Author-email: hychang.1997.tw@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Project-URL: Documentation, https://github.com/splasky/python-datastore-sqlalchemy/wiki
|
|
10
|
-
Project-URL: Source, https://github.com/splasky/python-datastore-sqlalchemy
|
|
11
|
-
Project-URL: Tracker, https://github.com/splasky/python-datastore-sqlalchemy/issues
|
|
12
|
-
Keywords: SQLAlchemy GCP Datastore
|
|
13
|
-
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
-
Classifier: Topic :: Database :: Front-Ends
|
|
19
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
-
Requires-Python: >=3.9
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: sqlalchemy<3.0.0,>=1.4.16
|
|
24
|
-
Dynamic: author
|
|
25
|
-
Dynamic: author-email
|
|
26
|
-
Dynamic: classifier
|
|
27
|
-
Dynamic: description
|
|
28
|
-
Dynamic: description-content-type
|
|
29
|
-
Dynamic: home-page
|
|
30
|
-
Dynamic: keywords
|
|
31
|
-
Dynamic: license
|
|
32
|
-
Dynamic: license-file
|
|
33
|
-
Dynamic: project-url
|
|
34
|
-
Dynamic: requires-dist
|
|
35
|
-
Dynamic: requires-python
|
|
36
|
-
Dynamic: summary
|
|
37
|
-
|
|
38
|
-
SQLAlchemy dialect for google cloud datastore(firestore mode)
|
|
39
|
-
========================
|
|
40
|
-
How to install
|
|
41
|
-
```
|
|
42
|
-
python3 setup.py install
|
|
43
|
-
```
|
|
44
|
-
How to use
|
|
45
|
-
```python
|
|
46
|
-
from sqlalchemy import *
|
|
47
|
-
from sqlalchemy.engine import create_engine
|
|
48
|
-
from sqlalchemy.schema import *
|
|
49
|
-
engine = create_engine('datastore://test-api-1', credentials='path/to/credentials.json')
|
|
50
|
-
conn = engine.connect()
|
|
51
|
-
result = conn.execute("SELECT * from test_table)
|
|
52
|
-
print(result.fetchall())
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Preview
|
|
56
|
-
<img src="assets/pie.png">
|
|
57
|
-
|
|
58
|
-
> [!WARNING]
|
|
59
|
-
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
60
|
-
|
|
61
|
-
## How to contribute
|
|
62
|
-
Feel free to open issues and pull requests on GitHub.
|
|
63
|
-
|
|
64
|
-
## Development Notes
|
|
65
|
-
- [Develop a SQLAlchemy and it's dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
SQLAlchemy dialect for google cloud datastore(firestore mode)
|
|
2
|
-
========================
|
|
3
|
-
How to install
|
|
4
|
-
```
|
|
5
|
-
python3 setup.py install
|
|
6
|
-
```
|
|
7
|
-
How to use
|
|
8
|
-
```python
|
|
9
|
-
from sqlalchemy import *
|
|
10
|
-
from sqlalchemy.engine import create_engine
|
|
11
|
-
from sqlalchemy.schema import *
|
|
12
|
-
engine = create_engine('datastore://test-api-1', credentials='path/to/credentials.json')
|
|
13
|
-
conn = engine.connect()
|
|
14
|
-
result = conn.execute("SELECT * from test_table)
|
|
15
|
-
print(result.fetchall())
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Preview
|
|
19
|
-
<img src="assets/pie.png">
|
|
20
|
-
|
|
21
|
-
> [!WARNING]
|
|
22
|
-
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
23
|
-
|
|
24
|
-
## How to contribute
|
|
25
|
-
Feel free to open issues and pull requests on GitHub.
|
|
26
|
-
|
|
27
|
-
## Development Notes
|
|
28
|
-
- [Develop a SQLAlchemy and it's dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[tool.ruff]
|
|
2
|
-
line-length = 120
|
|
3
|
-
|
|
4
|
-
[tool.ruff.lint]
|
|
5
|
-
select = ["E", "F", "W", "I", "N", "B", "C", "G", "S"]
|
|
6
|
-
ignore = [
|
|
7
|
-
"E501", # Line too long - allow longer lines for SQL strings in tests
|
|
8
|
-
"S608", # SQL injection - intentional for test cases
|
|
9
|
-
"B007", # Loop variable not used - common in test assertions
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
[tool.ruff.lint.per-file-ignores]
|
|
13
|
-
"tests/*" = ["S608", "E501", "S101", "B007", "S603"]
|
|
14
|
-
|
|
15
|
-
[tool.mypy]
|
|
16
|
-
ignore_missing_imports = true
|
|
17
|
-
disable_error_code = ["var-annotated", "import-untyped"]
|
|
18
|
-
|
|
19
|
-
[[tool.mypy.overrides]]
|
|
20
|
-
module = ["tests.*", "sqlalchemy_datastore.*"]
|
|
21
|
-
ignore_errors = true
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python-datastore-sqlalchemy
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: SQLAlchemy dialect for google cloud datastore
|
|
5
|
-
Home-page: https://github.com/splasky/python-datastore-sqlalchemy
|
|
6
|
-
Author: HY Chang(splasky)
|
|
7
|
-
Author-email: hychang.1997.tw@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Project-URL: Documentation, https://github.com/splasky/python-datastore-sqlalchemy/wiki
|
|
10
|
-
Project-URL: Source, https://github.com/splasky/python-datastore-sqlalchemy
|
|
11
|
-
Project-URL: Tracker, https://github.com/splasky/python-datastore-sqlalchemy/issues
|
|
12
|
-
Keywords: SQLAlchemy GCP Datastore
|
|
13
|
-
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
-
Classifier: Topic :: Database :: Front-Ends
|
|
19
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
-
Requires-Python: >=3.9
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: sqlalchemy<3.0.0,>=1.4.16
|
|
24
|
-
Dynamic: author
|
|
25
|
-
Dynamic: author-email
|
|
26
|
-
Dynamic: classifier
|
|
27
|
-
Dynamic: description
|
|
28
|
-
Dynamic: description-content-type
|
|
29
|
-
Dynamic: home-page
|
|
30
|
-
Dynamic: keywords
|
|
31
|
-
Dynamic: license
|
|
32
|
-
Dynamic: license-file
|
|
33
|
-
Dynamic: project-url
|
|
34
|
-
Dynamic: requires-dist
|
|
35
|
-
Dynamic: requires-python
|
|
36
|
-
Dynamic: summary
|
|
37
|
-
|
|
38
|
-
SQLAlchemy dialect for google cloud datastore(firestore mode)
|
|
39
|
-
========================
|
|
40
|
-
How to install
|
|
41
|
-
```
|
|
42
|
-
python3 setup.py install
|
|
43
|
-
```
|
|
44
|
-
How to use
|
|
45
|
-
```python
|
|
46
|
-
from sqlalchemy import *
|
|
47
|
-
from sqlalchemy.engine import create_engine
|
|
48
|
-
from sqlalchemy.schema import *
|
|
49
|
-
engine = create_engine('datastore://test-api-1', credentials='path/to/credentials.json')
|
|
50
|
-
conn = engine.connect()
|
|
51
|
-
result = conn.execute("SELECT * from test_table)
|
|
52
|
-
print(result.fetchall())
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Preview
|
|
56
|
-
<img src="assets/pie.png">
|
|
57
|
-
|
|
58
|
-
> [!WARNING]
|
|
59
|
-
> Please note: Not all GQL and SQL syntax has been fully tested. Should you encounter any bugs, please post the relevant query and open an issue on GitHub.
|
|
60
|
-
|
|
61
|
-
## How to contribute
|
|
62
|
-
Feel free to open issues and pull requests on GitHub.
|
|
63
|
-
|
|
64
|
-
## Development Notes
|
|
65
|
-
- [Develop a SQLAlchemy and it's dialects](https://hackmd.io/lsBW5GCVR82SORyWZ1cssA?view)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
sqlalchemy<3.0.0,>=1.4.16
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2025 hychang <hychang.1997.tw@gmail.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
-
# this software and associated documentation files (the "Software"), to deal in
|
|
5
|
-
# the Software without restriction, including without limitation the rights to
|
|
6
|
-
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
-
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
-
# subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
-
# copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
-
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
-
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
-
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
-
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
-
|
|
20
|
-
import os
|
|
21
|
-
import re
|
|
22
|
-
|
|
23
|
-
from setuptools import setup, find_packages
|
|
24
|
-
|
|
25
|
-
v = open(
|
|
26
|
-
os.path.join(os.path.dirname(__file__), "sqlalchemy_datastore", "__init__.py")
|
|
27
|
-
)
|
|
28
|
-
version_match = re.compile(r'.*__version__ = "(.*?)"', re.S).match(v.read())
|
|
29
|
-
if not version_match:
|
|
30
|
-
raise RuntimeError("Unable to find version string in __init__.py.")
|
|
31
|
-
VERSION = version_match.group(1)
|
|
32
|
-
v.close()
|
|
33
|
-
|
|
34
|
-
readme = os.path.join(os.path.dirname(__file__), "README.md")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
setup(
|
|
38
|
-
name="python-datastore-sqlalchemy",
|
|
39
|
-
version=VERSION,
|
|
40
|
-
description="SQLAlchemy dialect for google cloud datastore",
|
|
41
|
-
long_description=open(readme).read(),
|
|
42
|
-
long_description_content_type="text/markdown",
|
|
43
|
-
url="https://github.com/splasky/python-datastore-sqlalchemy",
|
|
44
|
-
author="HY Chang(splasky)",
|
|
45
|
-
author_email="hychang.1997.tw@gmail.com",
|
|
46
|
-
license="MIT",
|
|
47
|
-
classifiers=[
|
|
48
|
-
"Development Status :: 2 - Pre-Alpha",
|
|
49
|
-
"Intended Audience :: Developers",
|
|
50
|
-
"Programming Language :: Python",
|
|
51
|
-
"Programming Language :: Python :: 3",
|
|
52
|
-
"Programming Language :: Python :: Implementation :: CPython",
|
|
53
|
-
"Topic :: Database :: Front-Ends",
|
|
54
|
-
"Operating System :: POSIX :: Linux",
|
|
55
|
-
],
|
|
56
|
-
keywords="SQLAlchemy GCP Datastore",
|
|
57
|
-
python_requires=">=3.9",
|
|
58
|
-
project_urls={
|
|
59
|
-
"Documentation": "https://github.com/splasky/python-datastore-sqlalchemy/wiki",
|
|
60
|
-
"Source": "https://github.com/splasky/python-datastore-sqlalchemy",
|
|
61
|
-
"Tracker": "https://github.com/splasky/python-datastore-sqlalchemy/issues",
|
|
62
|
-
},
|
|
63
|
-
packages=find_packages(include=["sqlalchemy_datastore"]),
|
|
64
|
-
include_package_data=True,
|
|
65
|
-
install_requires=["sqlalchemy>=1.4.16,<3.0.0",],
|
|
66
|
-
zip_safe=False,
|
|
67
|
-
entry_points={
|
|
68
|
-
"sqlalchemy.dialects": ["datastore = sqlalchemy_datastore:CloudDatastoreDialect"]
|
|
69
|
-
},
|
|
70
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_datastore.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_dbapi.py
RENAMED
|
File without changes
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_helpers.py
RENAMED
|
File without changes
|
{python_datastore_sqlalchemy-0.0.1 → python_datastore_sqlalchemy-0.0.3}/tests/test_unit_types.py
RENAMED
|
File without changes
|