pyrsql 0.0.1__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.
- pyrsql-0.0.1/PKG-INFO +72 -0
- pyrsql-0.0.1/README.md +43 -0
- pyrsql-0.0.1/docs/architecture.md +461 -0
- pyrsql-0.0.1/docs/pyguide.md +3709 -0
- pyrsql-0.0.1/docs/quality.md +94 -0
- pyrsql-0.0.1/docs/reference.md +199 -0
- pyrsql-0.0.1/docs/testing.md +228 -0
- pyrsql-0.0.1/docs/usage.md +631 -0
- pyrsql-0.0.1/pyproject.toml +117 -0
- pyrsql-0.0.1/src/pyrsql/__init__.py +63 -0
- pyrsql-0.0.1/src/pyrsql/adapters/__init__.py +3 -0
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/__init__.py +15 -0
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/config.py +76 -0
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/criteria.py +56 -0
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/dependency.py +217 -0
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/errors.py +107 -0
- pyrsql-0.0.1/src/pyrsql/api.py +76 -0
- pyrsql-0.0.1/src/pyrsql/core/__init__.py +45 -0
- pyrsql-0.0.1/src/pyrsql/core/compiler.py +81 -0
- pyrsql-0.0.1/src/pyrsql/core/conversion.py +206 -0
- pyrsql-0.0.1/src/pyrsql/core/custom.py +14 -0
- pyrsql-0.0.1/src/pyrsql/core/field_policy.py +97 -0
- pyrsql-0.0.1/src/pyrsql/core/joins.py +11 -0
- pyrsql-0.0.1/src/pyrsql/core/json/__init__.py +19 -0
- pyrsql-0.0.1/src/pyrsql/core/json/options.py +31 -0
- pyrsql-0.0.1/src/pyrsql/core/json/path.py +35 -0
- pyrsql-0.0.1/src/pyrsql/core/json/query.py +39 -0
- pyrsql-0.0.1/src/pyrsql/core/json/values.py +72 -0
- pyrsql-0.0.1/src/pyrsql/core/options.py +416 -0
- pyrsql-0.0.1/src/pyrsql/core/page.py +115 -0
- pyrsql-0.0.1/src/pyrsql/core/procedure_policy.py +61 -0
- pyrsql-0.0.1/src/pyrsql/core/query.py +160 -0
- pyrsql-0.0.1/src/pyrsql/core/sort.py +153 -0
- pyrsql-0.0.1/src/pyrsql/integrations/__init__.py +8 -0
- pyrsql-0.0.1/src/pyrsql/integrations/fastapi/__init__.py +8 -0
- pyrsql-0.0.1/src/pyrsql/integrations/fastapi/sqlalchemy.py +251 -0
- pyrsql-0.0.1/src/pyrsql/orms/__init__.py +15 -0
- pyrsql-0.0.1/src/pyrsql/orms/base.py +99 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/__init__.py +39 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/coercion.py +57 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/compiled.py +56 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/compiled_page.py +40 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/compiled_sort.py +56 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/custom.py +25 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/errors.py +17 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/introspection.py +107 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/json_path.py +351 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/orm.py +65 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/resolver.py +202 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/sorter.py +173 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/statement.py +40 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/translator.py +560 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/type_inference.py +27 -0
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/types.py +64 -0
- pyrsql-0.0.1/src/pyrsql/parsing/__init__.py +32 -0
- pyrsql-0.0.1/src/pyrsql/parsing/ast.py +61 -0
- pyrsql-0.0.1/src/pyrsql/parsing/errors.py +25 -0
- pyrsql-0.0.1/src/pyrsql/parsing/lexer.py +264 -0
- pyrsql-0.0.1/src/pyrsql/parsing/limits.py +15 -0
- pyrsql-0.0.1/src/pyrsql/parsing/operators.py +201 -0
- pyrsql-0.0.1/src/pyrsql/parsing/parser.py +292 -0
- pyrsql-0.0.1/src/pyrsql/parsing/source.py +48 -0
- pyrsql-0.0.1/src/pyrsql/parsing/tokens.py +30 -0
- pyrsql-0.0.1/src/pyrsql/py.typed +0 -0
- pyrsql-0.0.1/src/pyrsql/selector/__init__.py +30 -0
- pyrsql-0.0.1/src/pyrsql/selector/analyzer.py +58 -0
- pyrsql-0.0.1/src/pyrsql/selector/ast.py +31 -0
- pyrsql-0.0.1/src/pyrsql/selector/parser.py +130 -0
- pyrsql-0.0.1/src/pyrsql/selector/semantic.py +34 -0
- pyrsql-0.0.1/src/pyrsql/semantic/__init__.py +23 -0
- pyrsql-0.0.1/src/pyrsql/semantic/analyzer.py +116 -0
- pyrsql-0.0.1/src/pyrsql/semantic/ast.py +35 -0
- pyrsql-0.0.1/src/pyrsql/semantic/errors.py +40 -0
- pyrsql-0.0.1/src/pyrsql/sorting/__init__.py +26 -0
- pyrsql-0.0.1/src/pyrsql/sorting/analyzer.py +74 -0
- pyrsql-0.0.1/src/pyrsql/sorting/ast.py +46 -0
- pyrsql-0.0.1/src/pyrsql/sorting/errors.py +21 -0
- pyrsql-0.0.1/src/pyrsql/sorting/limits.py +15 -0
- pyrsql-0.0.1/src/pyrsql/sorting/parser.py +150 -0
- pyrsql-0.0.1/src/pyrsql/sorting/semantic.py +15 -0
- pyrsql-0.0.1/tests/conftest.py +31 -0
- pyrsql-0.0.1/tests/fixtures/__init__.py +1 -0
- pyrsql-0.0.1/tests/fixtures/orms.py +1 -0
- pyrsql-0.0.1/tests/fixtures/query_samples.py +1 -0
- pyrsql-0.0.1/tests/fixtures/sqlalchemy_models.py +1 -0
- pyrsql-0.0.1/tests/functional/__init__.py +1 -0
- pyrsql-0.0.1/tests/functional/fastapi_adapter/__init__.py +1 -0
- pyrsql-0.0.1/tests/functional/fastapi_adapter/test_dependency.py +196 -0
- pyrsql-0.0.1/tests/functional/fastapi_sqlalchemy/__init__.py +1 -0
- pyrsql-0.0.1/tests/functional/fastapi_sqlalchemy/test_integration.py +126 -0
- pyrsql-0.0.1/tests/functional/test_public_api.py +80 -0
- pyrsql-0.0.1/tests/integration/__init__.py +1 -0
- pyrsql-0.0.1/tests/integration/sqlalchemy/__init__.py +1 -0
- pyrsql-0.0.1/tests/integration/sqlalchemy/test_page_pipeline.py +65 -0
- pyrsql-0.0.1/tests/integration/sqlalchemy/test_query_pipeline.py +521 -0
- pyrsql-0.0.1/tests/integration/sqlalchemy/test_sort_pipeline.py +177 -0
- pyrsql-0.0.1/tests/performance/__init__.py +1 -0
- pyrsql-0.0.1/tests/performance/test_conversion_bench.py +40 -0
- pyrsql-0.0.1/tests/performance/test_json_bench.py +55 -0
- pyrsql-0.0.1/tests/performance/test_parser_bench.py +35 -0
- pyrsql-0.0.1/tests/performance/test_selector_bench.py +27 -0
- pyrsql-0.0.1/tests/performance/test_semantic_bench.py +36 -0
- pyrsql-0.0.1/tests/performance/test_sqlalchemy_translation_bench.py +68 -0
- pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_config.py +40 -0
- pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_criteria.py +83 -0
- pyrsql-0.0.1/tests/unit/core/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/core/json/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/core/json/test_options.py +32 -0
- pyrsql-0.0.1/tests/unit/core/json/test_path.py +20 -0
- pyrsql-0.0.1/tests/unit/core/json/test_query.py +28 -0
- pyrsql-0.0.1/tests/unit/core/json/test_values.py +21 -0
- pyrsql-0.0.1/tests/unit/core/test_conversion.py +109 -0
- pyrsql-0.0.1/tests/unit/core/test_options.py +160 -0
- pyrsql-0.0.1/tests/unit/core/test_page.py +99 -0
- pyrsql-0.0.1/tests/unit/core/test_query.py +87 -0
- pyrsql-0.0.1/tests/unit/core/test_sort.py +62 -0
- pyrsql-0.0.1/tests/unit/integrations/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/integrations/fastapi/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/integrations/fastapi_sqlalchemy/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +136 -0
- pyrsql-0.0.1/tests/unit/orms/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/orms/sqlalchemy/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/orms/sqlalchemy/test_resolver.py +187 -0
- pyrsql-0.0.1/tests/unit/parsing/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/parsing/test_lexer.py +78 -0
- pyrsql-0.0.1/tests/unit/parsing/test_parser.py +108 -0
- pyrsql-0.0.1/tests/unit/selector/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/selector/test_parser.py +13 -0
- pyrsql-0.0.1/tests/unit/semantic/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/semantic/test_analyzer.py +93 -0
- pyrsql-0.0.1/tests/unit/sorting/__init__.py +1 -0
- pyrsql-0.0.1/tests/unit/sorting/test_analyzer.py +80 -0
- pyrsql-0.0.1/tests/unit/sorting/test_parser.py +81 -0
pyrsql-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrsql
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: ORM-oriented RSQL support for Python applications.
|
|
5
|
+
Keywords: rsql,orm,sqlalchemy,fastapi,query-language,rest
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Topic :: Database
|
|
16
|
+
Requires-Dist: ciso8601>=2.3,<3.0
|
|
17
|
+
Requires-Dist: msgspec>=0.18,<1.0
|
|
18
|
+
Requires-Dist: typing-extensions>=4.10.0 ; python_full_version < '3.11'
|
|
19
|
+
Requires-Dist: fastapi>=0.110,<1.0 ; extra == 'fastapi'
|
|
20
|
+
Requires-Dist: sqlalchemy>=2.0,<3.0 ; extra == 'sqlalchemy'
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Project-URL: Homepage, https://github.com/wskr00/pyrsql
|
|
23
|
+
Project-URL: Repository, https://github.com/wskr00/pyrsql
|
|
24
|
+
Project-URL: Issues, https://github.com/wskr00/pyrsql/issues
|
|
25
|
+
Project-URL: Documentation, https://github.com/wskr00/pyrsql/blob/main/README.md
|
|
26
|
+
Provides-Extra: fastapi
|
|
27
|
+
Provides-Extra: sqlalchemy
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
## pyrsql
|
|
31
|
+
|
|
32
|
+
`pyrsql` is an ORM-oriented RSQL library for Python.
|
|
33
|
+
|
|
34
|
+
The project is being designed to support multiple ORMs, starting with
|
|
35
|
+
`SQLAlchemy 2.0`. The core package remains ORM-neutral, while ORM
|
|
36
|
+
implementations live in `orms`, and framework-specific integration lives
|
|
37
|
+
in `adapters`.
|
|
38
|
+
|
|
39
|
+
Current `SQLAlchemy` support includes:
|
|
40
|
+
|
|
41
|
+
- filters
|
|
42
|
+
- sorting
|
|
43
|
+
- pagination
|
|
44
|
+
- `distinct`
|
|
45
|
+
- `join_hints`
|
|
46
|
+
- PostgreSQL-style JSON / JSONB path support
|
|
47
|
+
- temporal JSON path semantics via `JSONOptions(use_datetime=True)`
|
|
48
|
+
|
|
49
|
+
Current adapter support includes:
|
|
50
|
+
|
|
51
|
+
- `FastAPI` request extraction via dependencies
|
|
52
|
+
- configurable `filter`, `sort`, `page`, and `size` parameters
|
|
53
|
+
- class-based and factory-based FastAPI dependencies
|
|
54
|
+
- `HTTP 422` translation for pyrsql parse and semantic failures
|
|
55
|
+
|
|
56
|
+
## Development Principles
|
|
57
|
+
|
|
58
|
+
- Object-oriented design
|
|
59
|
+
- SOLID principles
|
|
60
|
+
- Google Python Style Guide
|
|
61
|
+
- Strong typing
|
|
62
|
+
- ORM-neutral public API
|
|
63
|
+
|
|
64
|
+
## Documentation
|
|
65
|
+
|
|
66
|
+
- [Architecture](docs/architecture.md)
|
|
67
|
+
- [Usage](docs/usage.md)
|
|
68
|
+
- [Reference](docs/reference.md)
|
|
69
|
+
- [FastAPI Adapter Usage](docs/usage.md#fastapi-adapter)
|
|
70
|
+
- [Google Python Style Guide](docs/pyguide.md)
|
|
71
|
+
- [Quality and Tooling](docs/quality.md)
|
|
72
|
+
- [Testing](docs/testing.md)
|
pyrsql-0.0.1/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## pyrsql
|
|
2
|
+
|
|
3
|
+
`pyrsql` is an ORM-oriented RSQL library for Python.
|
|
4
|
+
|
|
5
|
+
The project is being designed to support multiple ORMs, starting with
|
|
6
|
+
`SQLAlchemy 2.0`. The core package remains ORM-neutral, while ORM
|
|
7
|
+
implementations live in `orms`, and framework-specific integration lives
|
|
8
|
+
in `adapters`.
|
|
9
|
+
|
|
10
|
+
Current `SQLAlchemy` support includes:
|
|
11
|
+
|
|
12
|
+
- filters
|
|
13
|
+
- sorting
|
|
14
|
+
- pagination
|
|
15
|
+
- `distinct`
|
|
16
|
+
- `join_hints`
|
|
17
|
+
- PostgreSQL-style JSON / JSONB path support
|
|
18
|
+
- temporal JSON path semantics via `JSONOptions(use_datetime=True)`
|
|
19
|
+
|
|
20
|
+
Current adapter support includes:
|
|
21
|
+
|
|
22
|
+
- `FastAPI` request extraction via dependencies
|
|
23
|
+
- configurable `filter`, `sort`, `page`, and `size` parameters
|
|
24
|
+
- class-based and factory-based FastAPI dependencies
|
|
25
|
+
- `HTTP 422` translation for pyrsql parse and semantic failures
|
|
26
|
+
|
|
27
|
+
## Development Principles
|
|
28
|
+
|
|
29
|
+
- Object-oriented design
|
|
30
|
+
- SOLID principles
|
|
31
|
+
- Google Python Style Guide
|
|
32
|
+
- Strong typing
|
|
33
|
+
- ORM-neutral public API
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
- [Architecture](docs/architecture.md)
|
|
38
|
+
- [Usage](docs/usage.md)
|
|
39
|
+
- [Reference](docs/reference.md)
|
|
40
|
+
- [FastAPI Adapter Usage](docs/usage.md#fastapi-adapter)
|
|
41
|
+
- [Google Python Style Guide](docs/pyguide.md)
|
|
42
|
+
- [Quality and Tooling](docs/quality.md)
|
|
43
|
+
- [Testing](docs/testing.md)
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# pyrsql Architecture and Project Decisions
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
`pyrsql` is a Python library that translates RSQL-like queries into
|
|
6
|
+
ORM-specific query objects.
|
|
7
|
+
|
|
8
|
+
The project is being designed from the start to support multiple ORMs in
|
|
9
|
+
the future, such as:
|
|
10
|
+
|
|
11
|
+
- `SQLAlchemy`
|
|
12
|
+
- `Django ORM`
|
|
13
|
+
- other ORMs if they fit the abstraction model
|
|
14
|
+
|
|
15
|
+
The initial implementation target is `SQLAlchemy 2.0`.
|
|
16
|
+
|
|
17
|
+
## Current Scope
|
|
18
|
+
|
|
19
|
+
The current focus is:
|
|
20
|
+
|
|
21
|
+
- define the project architecture correctly before implementation
|
|
22
|
+
- keep the core ORM-oriented
|
|
23
|
+
- implement the first production ORM for `SQLAlchemy 2.0`
|
|
24
|
+
|
|
25
|
+
The following items are intentionally out of the MVP:
|
|
26
|
+
|
|
27
|
+
- `JSON` / `JSONB`
|
|
28
|
+
- additional ORMs
|
|
29
|
+
- exact feature parity with `rsql-jpa`
|
|
30
|
+
|
|
31
|
+
## High-Level Product Direction
|
|
32
|
+
|
|
33
|
+
`pyrsql` should be:
|
|
34
|
+
|
|
35
|
+
- object-oriented
|
|
36
|
+
- strongly typed
|
|
37
|
+
- extensible
|
|
38
|
+
- safe by default
|
|
39
|
+
- performant
|
|
40
|
+
- easy to understand and maintain
|
|
41
|
+
|
|
42
|
+
The API should use clean, stable, high-level names. ORM-specific behavior
|
|
43
|
+
must not leak into the public API naming unnecessarily.
|
|
44
|
+
|
|
45
|
+
## Core Architecture Principle
|
|
46
|
+
|
|
47
|
+
The project must be split into:
|
|
48
|
+
|
|
49
|
+
- a ORM-oriented core
|
|
50
|
+
- one or more ORM implementations
|
|
51
|
+
- one or more framework adapters
|
|
52
|
+
|
|
53
|
+
The core must not import or depend on `SQLAlchemy` or any other ORM
|
|
54
|
+
implementation.
|
|
55
|
+
|
|
56
|
+
ORMs depend on the core, not the other way around.
|
|
57
|
+
|
|
58
|
+
Framework adapters also depend on the core, and may depend on one or more
|
|
59
|
+
ORMs.
|
|
60
|
+
|
|
61
|
+
This separation is required so that:
|
|
62
|
+
|
|
63
|
+
- `pip install pyrsql` installs only the core
|
|
64
|
+
- `pip install pyrsql[sqlalchemy]` enables SQLAlchemy integration
|
|
65
|
+
- `pip install pyrsql[fastapi,sqlalchemy]` enables HTTP adapter plus query
|
|
66
|
+
ORM
|
|
67
|
+
- future ORMs can be added without restructuring the project
|
|
68
|
+
|
|
69
|
+
## Architectural Style
|
|
70
|
+
|
|
71
|
+
The project will follow:
|
|
72
|
+
|
|
73
|
+
- object-oriented design
|
|
74
|
+
- SOLID principles
|
|
75
|
+
- Google Python Style Guide
|
|
76
|
+
|
|
77
|
+
### SOLID Interpretation for `pyrsql`
|
|
78
|
+
|
|
79
|
+
#### Single Responsibility Principle
|
|
80
|
+
|
|
81
|
+
Each major concern must live in its own component. For example:
|
|
82
|
+
|
|
83
|
+
- lexical analysis
|
|
84
|
+
- parsing
|
|
85
|
+
- AST modeling
|
|
86
|
+
- semantic validation
|
|
87
|
+
- type coercion
|
|
88
|
+
- ORM translation
|
|
89
|
+
- ORM application to a query object
|
|
90
|
+
- framework request and error adaptation
|
|
91
|
+
|
|
92
|
+
#### Open/Closed Principle
|
|
93
|
+
|
|
94
|
+
The system must be open for extension through new ORMs, adapters, and
|
|
95
|
+
custom strategies, while avoiding modification of the core design when new
|
|
96
|
+
capabilities are introduced.
|
|
97
|
+
|
|
98
|
+
#### Liskov Substitution Principle
|
|
99
|
+
|
|
100
|
+
ORM integrations must honor clear behavioral contracts so one ORM implementation
|
|
101
|
+
can be replaced by another without breaking the API semantics.
|
|
102
|
+
|
|
103
|
+
#### Interface Segregation Principle
|
|
104
|
+
|
|
105
|
+
Interfaces must remain small and focused. Avoid one oversized abstraction that
|
|
106
|
+
handles parsing, semantic analysis, translation, and execution concerns at
|
|
107
|
+
once.
|
|
108
|
+
|
|
109
|
+
#### Dependency Inversion Principle
|
|
110
|
+
|
|
111
|
+
The core must depend on abstractions, such as `Protocol` or `ABC` contracts,
|
|
112
|
+
rather than concrete ORM implementations.
|
|
113
|
+
|
|
114
|
+
## Public API Direction
|
|
115
|
+
|
|
116
|
+
The public API should remain small, expressive, and orm-neutral.
|
|
117
|
+
|
|
118
|
+
Preferred public names:
|
|
119
|
+
|
|
120
|
+
- `parse(...)`
|
|
121
|
+
- `compile(...)`
|
|
122
|
+
- `apply(...)`
|
|
123
|
+
|
|
124
|
+
The naming of public functions should not vary by orm. For example, avoid
|
|
125
|
+
names such as:
|
|
126
|
+
|
|
127
|
+
- `apply_sqlalchemy(...)`
|
|
128
|
+
- `compile_django(...)`
|
|
129
|
+
|
|
130
|
+
Instead, ORM specialization should happen through injected ORM objects
|
|
131
|
+
or ORM-specific configured objects.
|
|
132
|
+
|
|
133
|
+
## Object-Oriented API Direction
|
|
134
|
+
|
|
135
|
+
The project should prefer high-level object-oriented composition over a purely
|
|
136
|
+
procedural style.
|
|
137
|
+
|
|
138
|
+
Representative domain objects may include:
|
|
139
|
+
|
|
140
|
+
- `Query`
|
|
141
|
+
- `QueryOptions`
|
|
142
|
+
- `CompilationResult`
|
|
143
|
+
- `ORM`
|
|
144
|
+
|
|
145
|
+
Potential style directions include:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
query = Query.parse("name==john")
|
|
149
|
+
compiled = query.compile(orm=orm)
|
|
150
|
+
stmt = compiled.apply(stmt, model=User)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
or:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
stmt = apply(stmt, User, "name==john", orm=orm)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The exact public surface can still be refined, but it must stay:
|
|
160
|
+
|
|
161
|
+
- cohesive
|
|
162
|
+
- orm-neutral in naming
|
|
163
|
+
- explicit in contracts
|
|
164
|
+
|
|
165
|
+
## Parser Strategy
|
|
166
|
+
|
|
167
|
+
The parser will be built in-house rather than relying on an external RSQL
|
|
168
|
+
parser package.
|
|
169
|
+
|
|
170
|
+
### Parser goals
|
|
171
|
+
|
|
172
|
+
- high performance
|
|
173
|
+
- memory efficiency
|
|
174
|
+
- strong control over syntax and error handling
|
|
175
|
+
- secure behavior under malformed or adversarial input
|
|
176
|
+
|
|
177
|
+
### Parser design direction
|
|
178
|
+
|
|
179
|
+
- custom lexer
|
|
180
|
+
- custom parser
|
|
181
|
+
- immutable AST nodes
|
|
182
|
+
- configurable safety limits
|
|
183
|
+
|
|
184
|
+
Possible safety controls include:
|
|
185
|
+
|
|
186
|
+
- maximum query length
|
|
187
|
+
- maximum nesting depth
|
|
188
|
+
- maximum AST node count
|
|
189
|
+
- maximum argument count
|
|
190
|
+
|
|
191
|
+
## ORM Strategy
|
|
192
|
+
|
|
193
|
+
The first ORM is `SQLAlchemy 2.0`.
|
|
194
|
+
|
|
195
|
+
Important constraints:
|
|
196
|
+
|
|
197
|
+
- use the `2.0` query model
|
|
198
|
+
- target `select(...)` workflows
|
|
199
|
+
- do not design around legacy `Query`
|
|
200
|
+
|
|
201
|
+
Future ORMs are expected, including `django`, but the current
|
|
202
|
+
implementation work should optimize for `SQLAlchemy` first without
|
|
203
|
+
contaminating the core abstractions.
|
|
204
|
+
|
|
205
|
+
## Adapter Strategy
|
|
206
|
+
|
|
207
|
+
Framework-specific integration should live in adapters rather than ORMs.
|
|
208
|
+
|
|
209
|
+
Examples:
|
|
210
|
+
|
|
211
|
+
- `adapters.fastapi`
|
|
212
|
+
- `adapters.django`
|
|
213
|
+
|
|
214
|
+
The adapter layer is responsible for:
|
|
215
|
+
|
|
216
|
+
- request extraction
|
|
217
|
+
- transport-specific validation behavior
|
|
218
|
+
- transport-specific error conversion
|
|
219
|
+
- convenient binding to framework primitives
|
|
220
|
+
|
|
221
|
+
Adapters must not own the query language semantics. They orchestrate the core
|
|
222
|
+
and selected ORM for a specific runtime environment.
|
|
223
|
+
|
|
224
|
+
## Packaging Strategy
|
|
225
|
+
|
|
226
|
+
The package name is `pyrsql`.
|
|
227
|
+
|
|
228
|
+
The project should support optional ORM installations through extras:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
pip install pyrsql[sqlalchemy]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Future examples:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
pip install pyrsql[django]
|
|
238
|
+
pip install pyrsql[fastapi,sqlalchemy]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Packaging rules
|
|
242
|
+
|
|
243
|
+
- base package contains only the core and shared abstractions
|
|
244
|
+
- ORM dependencies are optional
|
|
245
|
+
- adapter dependencies are optional
|
|
246
|
+
- development tooling lives in a separate extra
|
|
247
|
+
|
|
248
|
+
Representative `pyproject.toml` direction:
|
|
249
|
+
|
|
250
|
+
```toml
|
|
251
|
+
[project.optional-dependencies]
|
|
252
|
+
sqlalchemy = ["sqlalchemy>=2.0"]
|
|
253
|
+
fastapi = ["fastapi>=0.110"]
|
|
254
|
+
django = ["django>=5.0"]
|
|
255
|
+
dev = ["pytest", "pylint", "mypy", "black", "isort"]
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Project Structure Direction
|
|
259
|
+
|
|
260
|
+
The package should be prepared for multiple ORMs from the beginning.
|
|
261
|
+
|
|
262
|
+
Suggested structure:
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
src/pyrsql/
|
|
266
|
+
__init__.py
|
|
267
|
+
api/
|
|
268
|
+
core/
|
|
269
|
+
parsing/
|
|
270
|
+
orms/
|
|
271
|
+
base.py
|
|
272
|
+
sqlalchemy/
|
|
273
|
+
__init__.py
|
|
274
|
+
orm.py
|
|
275
|
+
translator.py
|
|
276
|
+
resolver.py
|
|
277
|
+
django/
|
|
278
|
+
__init__.py
|
|
279
|
+
orm.py
|
|
280
|
+
translator.py
|
|
281
|
+
resolver.py
|
|
282
|
+
adapters/
|
|
283
|
+
fastapi/
|
|
284
|
+
__init__.py
|
|
285
|
+
dependency.py
|
|
286
|
+
errors.py
|
|
287
|
+
django/
|
|
288
|
+
__init__.py
|
|
289
|
+
request.py
|
|
290
|
+
errors.py
|
|
291
|
+
errors.py
|
|
292
|
+
|
|
293
|
+
docs/
|
|
294
|
+
tests/
|
|
295
|
+
unit/
|
|
296
|
+
integration/
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
This structure can still evolve, but the major boundaries should remain stable.
|
|
300
|
+
|
|
301
|
+
## Core Components
|
|
302
|
+
|
|
303
|
+
The ORM-oriented core is expected to contain:
|
|
304
|
+
|
|
305
|
+
- operators and operator metadata
|
|
306
|
+
- lexer
|
|
307
|
+
- parser
|
|
308
|
+
- AST
|
|
309
|
+
- semantic validation
|
|
310
|
+
- property remapping support
|
|
311
|
+
- whitelist and blacklist support
|
|
312
|
+
- type coercion infrastructure
|
|
313
|
+
- shared exceptions
|
|
314
|
+
- query and compilation abstractions
|
|
315
|
+
|
|
316
|
+
## ORM Components
|
|
317
|
+
|
|
318
|
+
The `SQLAlchemy` ORM is expected to contain:
|
|
319
|
+
|
|
320
|
+
- path resolution
|
|
321
|
+
- relationship and join planning
|
|
322
|
+
- AST to predicate translation
|
|
323
|
+
- application of compiled output to a `Select`
|
|
324
|
+
|
|
325
|
+
The ORM layer should handle ORM-specific behavior without leaking those
|
|
326
|
+
details into the core model.
|
|
327
|
+
|
|
328
|
+
Future ORMs are expected to follow the same responsibility split,
|
|
329
|
+
including `django` as a query orm.
|
|
330
|
+
|
|
331
|
+
## Adapter Components
|
|
332
|
+
|
|
333
|
+
Adapters are separate from ORM implementations.
|
|
334
|
+
|
|
335
|
+
Examples:
|
|
336
|
+
|
|
337
|
+
- `adapters.fastapi`
|
|
338
|
+
- `adapters.django`
|
|
339
|
+
|
|
340
|
+
Adapters should contain framework-facing concerns such as:
|
|
341
|
+
|
|
342
|
+
- extracting query text from requests
|
|
343
|
+
- mapping parsing and validation failures to framework errors
|
|
344
|
+
- offering convenience helpers for framework-native usage
|
|
345
|
+
|
|
346
|
+
An adapter may depend on a orm, but an ORM must not depend on an
|
|
347
|
+
adapter.
|
|
348
|
+
|
|
349
|
+
## Quality Standards
|
|
350
|
+
|
|
351
|
+
The project must be built with strict quality gates from the beginning.
|
|
352
|
+
|
|
353
|
+
Required tooling direction:
|
|
354
|
+
|
|
355
|
+
- `pytest`
|
|
356
|
+
- `pylint`
|
|
357
|
+
- `mypy`
|
|
358
|
+
- `black`
|
|
359
|
+
- `isort`
|
|
360
|
+
|
|
361
|
+
Additional quality expectations:
|
|
362
|
+
|
|
363
|
+
- strong unit coverage for core logic
|
|
364
|
+
- integration tests for ORM behavior
|
|
365
|
+
- clear exception model
|
|
366
|
+
- good internal naming
|
|
367
|
+
- minimal accidental complexity
|
|
368
|
+
|
|
369
|
+
## Code Style and Language Conventions
|
|
370
|
+
|
|
371
|
+
The baseline style reference is the Google Python Style Guide.
|
|
372
|
+
|
|
373
|
+
Key practical implications:
|
|
374
|
+
|
|
375
|
+
- clear names
|
|
376
|
+
- readable structure
|
|
377
|
+
- explicit responsibilities
|
|
378
|
+
- consistent docstrings
|
|
379
|
+
- predictable formatting
|
|
380
|
+
- typed interfaces where useful
|
|
381
|
+
|
|
382
|
+
Python features should be used when they improve correctness, readability,
|
|
383
|
+
architecture, or performance.
|
|
384
|
+
|
|
385
|
+
Examples of acceptable advanced usage:
|
|
386
|
+
|
|
387
|
+
- `dataclasses`
|
|
388
|
+
- `slots`
|
|
389
|
+
- `Protocol`
|
|
390
|
+
- `ABC`
|
|
391
|
+
- generics
|
|
392
|
+
- immutable value objects where appropriate
|
|
393
|
+
|
|
394
|
+
These features must be used deliberately, not performatively.
|
|
395
|
+
|
|
396
|
+
## Design Preferences
|
|
397
|
+
|
|
398
|
+
The project should avoid:
|
|
399
|
+
|
|
400
|
+
- giant procedural helper modules
|
|
401
|
+
- ORM-specific logic in the core
|
|
402
|
+
- adapter-specific transport logic in the core
|
|
403
|
+
- unstable or overly clever APIs
|
|
404
|
+
- naming that changes by ORM
|
|
405
|
+
- unnecessary exact compatibility constraints with `rsql-jpa`
|
|
406
|
+
|
|
407
|
+
The project should favor:
|
|
408
|
+
|
|
409
|
+
- explicit domain objects
|
|
410
|
+
- narrow contracts
|
|
411
|
+
- composition
|
|
412
|
+
- immutability where it helps
|
|
413
|
+
- extension points that do not distort the core model
|
|
414
|
+
|
|
415
|
+
## MVP Functional Direction
|
|
416
|
+
|
|
417
|
+
The first implementation phase should focus on:
|
|
418
|
+
|
|
419
|
+
- query parsing
|
|
420
|
+
- AST generation
|
|
421
|
+
- semantic validation
|
|
422
|
+
- SQLAlchemy 2.0 translation
|
|
423
|
+
- high-quality tests
|
|
424
|
+
|
|
425
|
+
Representative operator coverage for the MVP:
|
|
426
|
+
|
|
427
|
+
- equality and inequality
|
|
428
|
+
- comparison operators
|
|
429
|
+
- `IN` / `NOT IN`
|
|
430
|
+
- null checks
|
|
431
|
+
- `LIKE` / case-insensitive `LIKE`
|
|
432
|
+
- boolean composition with `AND` / `OR`
|
|
433
|
+
- parentheses
|
|
434
|
+
|
|
435
|
+
The operator set does not need to be a perfect clone of the Java project as
|
|
436
|
+
long as the model is coherent and useful.
|
|
437
|
+
|
|
438
|
+
## Deferred Features
|
|
439
|
+
|
|
440
|
+
These items are intentionally postponed:
|
|
441
|
+
|
|
442
|
+
- `JSON` / `JSONB`
|
|
443
|
+
- secondary ORMs
|
|
444
|
+
- advanced ORM-specific features that would distort the initial
|
|
445
|
+
architecture
|
|
446
|
+
|
|
447
|
+
## Documentation Direction
|
|
448
|
+
|
|
449
|
+
The project should keep a dedicated `docs/` folder.
|
|
450
|
+
|
|
451
|
+
Documentation should capture:
|
|
452
|
+
|
|
453
|
+
- architecture decisions
|
|
454
|
+
- public API decisions
|
|
455
|
+
- packaging decisions
|
|
456
|
+
- coding standards
|
|
457
|
+
- testing standards
|
|
458
|
+
- implementation phases
|
|
459
|
+
|
|
460
|
+
This file is the initial consolidation of the decisions made before coding
|
|
461
|
+
begins.
|