jsonql-py 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.
Files changed (39) hide show
  1. jsonql_py-0.1.0/.github/workflows/ci.yml +172 -0
  2. jsonql_py-0.1.0/.github/workflows/publish.yml +86 -0
  3. jsonql_py-0.1.0/.gitignore +5 -0
  4. jsonql_py-0.1.0/LICENSE +21 -0
  5. jsonql_py-0.1.0/PKG-INFO +261 -0
  6. jsonql_py-0.1.0/README.md +216 -0
  7. jsonql_py-0.1.0/pyproject.toml +84 -0
  8. jsonql_py-0.1.0/src/jsonql/__init__.py +142 -0
  9. jsonql_py-0.1.0/src/jsonql/adapters/__init__.py +42 -0
  10. jsonql_py-0.1.0/src/jsonql/adapters/base.py +300 -0
  11. jsonql_py-0.1.0/src/jsonql/adapters/django_adapter.py +98 -0
  12. jsonql_py-0.1.0/src/jsonql/adapters/django_mongo.py +94 -0
  13. jsonql_py-0.1.0/src/jsonql/adapters/fastapi_adapter.py +102 -0
  14. jsonql_py-0.1.0/src/jsonql/adapters/fastapi_mongo.py +66 -0
  15. jsonql_py-0.1.0/src/jsonql/adapters/flask_adapter.py +84 -0
  16. jsonql_py-0.1.0/src/jsonql/adapters/flask_mongo.py +67 -0
  17. jsonql_py-0.1.0/src/jsonql/adapters/mongo_base.py +389 -0
  18. jsonql_py-0.1.0/src/jsonql/builder.py +137 -0
  19. jsonql_py-0.1.0/src/jsonql/conditions.py +103 -0
  20. jsonql_py-0.1.0/src/jsonql/dialect.py +162 -0
  21. jsonql_py-0.1.0/src/jsonql/driver.py +26 -0
  22. jsonql_py-0.1.0/src/jsonql/engine.py +185 -0
  23. jsonql_py-0.1.0/src/jsonql/errors.py +63 -0
  24. jsonql_py-0.1.0/src/jsonql/factory.py +357 -0
  25. jsonql_py-0.1.0/src/jsonql/hydrator.py +170 -0
  26. jsonql_py-0.1.0/src/jsonql/logger.py +57 -0
  27. jsonql_py-0.1.0/src/jsonql/mongo_driver.py +82 -0
  28. jsonql_py-0.1.0/src/jsonql/mongo_transpiler.py +337 -0
  29. jsonql_py-0.1.0/src/jsonql/parser.py +185 -0
  30. jsonql_py-0.1.0/src/jsonql/transpiler.py +634 -0
  31. jsonql_py-0.1.0/src/jsonql/types.py +185 -0
  32. jsonql_py-0.1.0/src/jsonql/validator.py +226 -0
  33. jsonql_py-0.1.0/tests/__init__.py +0 -0
  34. jsonql_py-0.1.0/tests/test_builder.py +130 -0
  35. jsonql_py-0.1.0/tests/test_factory.py +189 -0
  36. jsonql_py-0.1.0/tests/test_hydrator.py +118 -0
  37. jsonql_py-0.1.0/tests/test_parser.py +181 -0
  38. jsonql_py-0.1.0/tests/test_transpiler.py +260 -0
  39. jsonql_py-0.1.0/tests/test_validator.py +142 -0
@@ -0,0 +1,172 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ env:
10
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Install dependencies
24
+ run: pip install -e ".[dev]"
25
+
26
+ - name: Ruff check
27
+ run: ruff check src/ tests/
28
+
29
+ - name: Ruff format check
30
+ run: ruff format --check src/ tests/
31
+
32
+ - name: Mypy
33
+ run: mypy src/jsonql/
34
+
35
+ test:
36
+ runs-on: ubuntu-latest
37
+ needs: lint
38
+ strategy:
39
+ matrix:
40
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+
44
+ - name: Set up Python ${{ matrix.python-version }}
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: ${{ matrix.python-version }}
48
+
49
+ - name: Install dependencies
50
+ run: pip install -e ".[dev]"
51
+
52
+ - name: Run tests
53
+ run: pytest --tb=short -q
54
+
55
+ build:
56
+ runs-on: ubuntu-latest
57
+ needs: test
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+
61
+ - name: Set up Python
62
+ uses: actions/setup-python@v5
63
+ with:
64
+ python-version: "3.12"
65
+
66
+ - name: Install build tools
67
+ run: pip install build
68
+
69
+ - name: Build package
70
+ run: python -m build
71
+
72
+ - name: Upload dist artifacts
73
+ uses: actions/upload-artifact@v4
74
+ with:
75
+ name: dist
76
+ path: dist/
77
+
78
+ integration:
79
+ runs-on: ubuntu-latest
80
+ needs: test
81
+ services:
82
+ postgres:
83
+ image: postgres:16
84
+ env:
85
+ POSTGRES_USER: jsonql
86
+ POSTGRES_PASSWORD: password
87
+ POSTGRES_DB: jsonql_test
88
+ ports:
89
+ - 5432:5432
90
+ options: >-
91
+ --health-cmd "pg_isready -U jsonql"
92
+ --health-interval 5s
93
+ --health-timeout 5s
94
+ --health-retries 10
95
+
96
+ strategy:
97
+ fail-fast: false
98
+ matrix:
99
+ adapter:
100
+ - name: py-flask-simple
101
+ port: 9200
102
+ test-path: tests/unified
103
+ - name: py-flask-lifecycle
104
+ port: 9201
105
+ test-path: tests/unified/lifecycle
106
+ - name: py-fastapi-simple
107
+ port: 9202
108
+ test-path: tests/unified
109
+ - name: py-fastapi-lifecycle
110
+ port: 9203
111
+ test-path: tests/unified/lifecycle
112
+ - name: py-django-simple
113
+ port: 9204
114
+ test-path: tests/unified
115
+ - name: py-django-lifecycle
116
+ port: 9205
117
+ test-path: tests/unified/lifecycle
118
+
119
+ steps:
120
+ - uses: actions/checkout@v4
121
+ with:
122
+ path: jsonql-py
123
+
124
+ - uses: actions/checkout@v4
125
+ with:
126
+ repository: JSONQL-Standard/jsonql-tests
127
+ token: ${{ secrets.JSONQL_TESTS_TOKEN }}
128
+ path: jsonql-tests
129
+
130
+ - name: Set up Python
131
+ uses: actions/setup-python@v5
132
+ with:
133
+ python-version: "3.12"
134
+
135
+ - name: Install jsonql-py from source
136
+ run: pip install -e ./jsonql-py
137
+
138
+ - name: Install server dependencies
139
+ run: |
140
+ pip install flask fastapi uvicorn django djangorestframework
141
+ pip install psycopg2-binary pymysql pymssql pymongo
142
+
143
+ - name: Install test runner dependencies
144
+ run: pip install -r jsonql-tests/requirements.txt
145
+
146
+ - name: Start ${{ matrix.adapter.name }}
147
+ run: |
148
+ cd jsonql-tests/integration-tests/${{ matrix.adapter.name }}
149
+ PYTHONPATH=${{ github.workspace }}/jsonql-tests/integration-tests/py-shared \
150
+ DB_TYPE=postgres \
151
+ DB_DSN="host=localhost dbname=jsonql_test user=jsonql password=password" \
152
+ JSONQL_SCHEMA_PATH=${{ github.workspace }}/jsonql-tests/fixtures/standard/schema.json \
153
+ PORT=${{ matrix.adapter.port }} \
154
+ python server.py &
155
+ # Wait for server to be ready
156
+ for i in $(seq 1 30); do
157
+ if curl -sf http://localhost:${{ matrix.adapter.port }}/health > /dev/null 2>&1; then
158
+ echo "Server ready"
159
+ break
160
+ fi
161
+ sleep 2
162
+ done
163
+
164
+ - name: Run compliance tests against ${{ matrix.adapter.name }}
165
+ run: |
166
+ cd jsonql-tests
167
+ python -m pytest tests/ \
168
+ --target=http://localhost:${{ matrix.adapter.port }} \
169
+ --db-type=postgres \
170
+ --db-dsn="postgresql://jsonql:password@localhost:5432/jsonql_test" \
171
+ --test-path=${{ matrix.adapter.test-path }} \
172
+ -q --tb=short
@@ -0,0 +1,86 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ env:
8
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
9
+
10
+ permissions:
11
+ contents: read
12
+ id-token: write # Required for trusted publishing
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Install build tools
26
+ run: pip install build
27
+
28
+ - name: Build package
29
+ run: python -m build
30
+
31
+ - name: Upload dist artifacts
32
+ uses: actions/upload-artifact@v4
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+
37
+ test:
38
+ runs-on: ubuntu-latest
39
+ needs: build
40
+ strategy:
41
+ matrix:
42
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+
46
+ - name: Set up Python ${{ matrix.python-version }}
47
+ uses: actions/setup-python@v5
48
+ with:
49
+ python-version: ${{ matrix.python-version }}
50
+
51
+ - name: Install dependencies
52
+ run: pip install -e ".[dev]"
53
+
54
+ - name: Run tests
55
+ run: pytest --tb=short -q
56
+
57
+ publish-testpypi:
58
+ runs-on: ubuntu-latest
59
+ needs: [build, test]
60
+ environment: testpypi
61
+ steps:
62
+ - name: Download dist artifacts
63
+ uses: actions/download-artifact@v4
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+
68
+ - name: Publish to TestPyPI
69
+ uses: pypa/gh-action-pypi-publish@release/v1
70
+ with:
71
+ repository-url: https://test.pypi.org/legacy/
72
+
73
+ publish-pypi:
74
+ if: ${{ !github.event.release.prerelease }}
75
+ runs-on: ubuntu-latest
76
+ needs: [build, test, publish-testpypi]
77
+ environment: pypi
78
+ steps:
79
+ - name: Download dist artifacts
80
+ uses: actions/download-artifact@v4
81
+ with:
82
+ name: dist
83
+ path: dist/
84
+
85
+ - name: Publish to PyPI
86
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,5 @@
1
+ .pyc
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 JSONQL-Standard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,261 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonql-py
3
+ Version: 0.1.0
4
+ Summary: Python SDK for JSONQL — a JSON-based query language for SQL databases
5
+ Project-URL: Homepage, https://github.com/JSONQL-Standard/jsonql-py
6
+ Project-URL: Documentation, https://jsonql.org
7
+ Project-URL: Repository, https://github.com/JSONQL-Standard/jsonql-py
8
+ Project-URL: Issues, https://github.com/JSONQL-Standard/jsonql-py/issues
9
+ Project-URL: Changelog, https://github.com/JSONQL-Standard/jsonql-py/blob/master/CHANGELOG.md
10
+ Author: JSONQL Standard
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: api,jsonql,orm,query,sql
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Database
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.0; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.4; extra == 'dev'
31
+ Provides-Extra: django
32
+ Requires-Dist: django>=4.0; extra == 'django'
33
+ Requires-Dist: djangorestframework>=3.14; extra == 'django'
34
+ Provides-Extra: fastapi
35
+ Requires-Dist: fastapi>=0.100; extra == 'fastapi'
36
+ Requires-Dist: uvicorn>=0.20; extra == 'fastapi'
37
+ Provides-Extra: flask
38
+ Requires-Dist: flask>=2.0; extra == 'flask'
39
+ Provides-Extra: mysql
40
+ Requires-Dist: mysql-connector-python>=8.0; extra == 'mysql'
41
+ Provides-Extra: postgres
42
+ Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
43
+ Provides-Extra: sqlite
44
+ Description-Content-Type: text/markdown
45
+
46
+ # jsonql-py
47
+
48
+ **Python SDK** for [JSONQL](https://github.com/jsonql-standard/jsonql-spec) — a JSON-based query language for SQL databases.
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install jsonql-py
54
+ ```
55
+
56
+ With framework extras:
57
+
58
+ ```bash
59
+ pip install jsonql-py[flask] # Flask adapter
60
+ pip install jsonql-py[fastapi] # FastAPI adapter
61
+ pip install jsonql-py[django] # Django REST adapter
62
+ pip install jsonql-py[postgres] # PostgreSQL driver support
63
+ ```
64
+
65
+ ## Quick Start
66
+
67
+ ### Query Builder
68
+
69
+ ```python
70
+ from jsonql import QueryBuilder
71
+ from jsonql.conditions import eq, gt, field, and_
72
+
73
+ query = (
74
+ QueryBuilder()
75
+ .from_table("users")
76
+ .select("id", "name", "email")
77
+ .where(and_(
78
+ field("age", gt(18)),
79
+ field("status", eq("active")),
80
+ ))
81
+ .order_by("name", "-age")
82
+ .limit(10)
83
+ .build()
84
+ )
85
+ ```
86
+
87
+ ### Mutation Builder
88
+
89
+ ```python
90
+ from jsonql import MutationBuilder
91
+
92
+ # Create
93
+ mutation = MutationBuilder().create({"name": "Alice", "age": 30}).build()
94
+
95
+ # Update
96
+ mutation = (
97
+ MutationBuilder()
98
+ .update({"name": "Bob"})
99
+ .where({"id": {"eq": 1}})
100
+ .build()
101
+ )
102
+
103
+ # Delete
104
+ mutation = MutationBuilder().delete().where({"id": {"eq": 1}}).build()
105
+ ```
106
+
107
+ ### Transpiler
108
+
109
+ ```python
110
+ from jsonql import Parser, SQLTranspiler
111
+
112
+ parser = Parser()
113
+ query = parser.parse({
114
+ "fields": ["id", "name"],
115
+ "where": {"status": {"eq": "active"}},
116
+ "sort": ["-name"],
117
+ "limit": 10,
118
+ })
119
+
120
+ transpiler = SQLTranspiler("postgres")
121
+ result = transpiler.transpile(query, "users")
122
+ print(result.sql) # SELECT "users"."id", "users"."name" FROM "users" WHERE "users"."status" = $1 ORDER BY "users"."name" DESC LIMIT 10
123
+ print(result.args) # ['active']
124
+ ```
125
+
126
+ ### Schema Validation
127
+
128
+ ```python
129
+ from jsonql import Validator, JsonQLQuery
130
+ from jsonql.types import JsonQLSchema, JsonQLTable, JsonQLField
131
+
132
+ schema = JsonQLSchema(tables={
133
+ "users": JsonQLTable(fields={
134
+ "id": JsonQLField(type="integer"),
135
+ "name": JsonQLField(type="string"),
136
+ "secret": JsonQLField(type="string", allow_select=False),
137
+ }),
138
+ })
139
+
140
+ validator = Validator(schema, "users")
141
+
142
+ # Returns ValidationResult
143
+ result = validator.validate(JsonQLQuery(fields=["id", "name"]))
144
+ assert result.valid
145
+
146
+ # Raises JsonQLValidationError
147
+ validator.validate_or_raise(JsonQLQuery(fields=["secret"]))
148
+ ```
149
+
150
+ ### Engine (Full Pipeline)
151
+
152
+ ```python
153
+ import asyncio
154
+ from jsonql import JsonQLEngine
155
+ from jsonql.types import parse_schema
156
+
157
+ schema = parse_schema({...}) # Your schema JSON
158
+
159
+ async def run_sql(sql: str, params: list) -> list[dict]:
160
+ # Your database execution logic
161
+ ...
162
+
163
+ engine = (
164
+ JsonQLEngine.builder()
165
+ .postgres()
166
+ .schema(schema)
167
+ .executor(run_sql)
168
+ .debug()
169
+ .build()
170
+ )
171
+
172
+ result = asyncio.run(engine.execute({"fields": ["id", "name"]}, "users"))
173
+ print(result["data"])
174
+ ```
175
+
176
+ ### Flask Adapter
177
+
178
+ ```python
179
+ from flask import Flask
180
+ from jsonql.adapters import create_flask_blueprint, AdapterOptions
181
+
182
+ app = Flask(__name__)
183
+
184
+ bp = create_flask_blueprint(AdapterOptions(
185
+ dialect="sqlite",
186
+ execute=run_sql,
187
+ schema=my_schema,
188
+ ))
189
+ app.register_blueprint(bp, url_prefix="/jsonql")
190
+ ```
191
+
192
+ ### FastAPI Adapter
193
+
194
+ ```python
195
+ from fastapi import FastAPI
196
+ from jsonql.adapters import create_fastapi_router, AdapterOptions
197
+
198
+ app = FastAPI()
199
+
200
+ router = create_fastapi_router(AdapterOptions(
201
+ dialect="postgres",
202
+ execute=run_sql,
203
+ schema=my_schema,
204
+ ))
205
+ app.include_router(router, prefix="/jsonql")
206
+ ```
207
+
208
+ ### Django Adapter
209
+
210
+ ```python
211
+ # urls.py
212
+ from django.urls import path
213
+ from jsonql.adapters import JsonQLDjangoView, AdapterOptions
214
+
215
+ options = AdapterOptions(dialect="postgres", execute=run_sql, schema=my_schema)
216
+
217
+ urlpatterns = [
218
+ path("jsonql/", JsonQLDjangoView.as_view(options=options)),
219
+ path("jsonql/<path:path>/", JsonQLDjangoView.as_view(options=options)),
220
+ ]
221
+ ```
222
+
223
+ ## Supported Dialects
224
+
225
+ | Dialect | Placeholder | Quoting | RETURNING |
226
+ |------------|-------------|------------|-----------|
227
+ | `postgres` | `$1, $2` | `"col"` | ✅ |
228
+ | `mysql` | `?, ?` | `` `col` ``| ❌ |
229
+ | `sqlite` | `?, ?` | `"col"` | ❌ |
230
+
231
+ ## Condition Helpers
232
+
233
+ ```python
234
+ from jsonql.conditions import (
235
+ eq, neq, gt, gte, lt, lte,
236
+ is_in, not_in, like, contains, starts_with, ends_with,
237
+ field, and_, or_, not_,
238
+ )
239
+ ```
240
+
241
+ ## Error Hierarchy
242
+
243
+ ```
244
+ JsonQLError
245
+ ├── JsonQLValidationError (code: VALIDATION_ERROR)
246
+ ├── JsonQLTranspileError (code: TRANSPILE_ERROR)
247
+ └── JsonQLExecutionError (code: EXECUTION_ERROR)
248
+ ```
249
+
250
+ ## Development
251
+
252
+ ```bash
253
+ pip install -e ".[dev]" # import name is still 'jsonql'
254
+ pytest
255
+ ruff check src/ tests/
256
+ mypy src/
257
+ ```
258
+
259
+ ## License
260
+
261
+ MIT