pyrsql 0.0.1__tar.gz → 0.0.2__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.2/PKG-INFO +225 -0
- pyrsql-0.0.2/README.md +190 -0
- pyrsql-0.0.2/docs/contributing.md +49 -0
- pyrsql-0.0.2/docs/explanation/architecture.md +352 -0
- pyrsql-0.0.2/docs/explanation/extensibility.md +72 -0
- pyrsql-0.0.2/docs/index.md +36 -0
- pyrsql-0.0.2/docs/quickstart.md +71 -0
- pyrsql-0.0.2/docs/reference/api.md +43 -0
- pyrsql-0.0.2/docs/reference/operators.md +37 -0
- pyrsql-0.0.2/docs/reference/options.md +66 -0
- pyrsql-0.0.2/docs/testing.md +152 -0
- pyrsql-0.0.2/docs/usage/custom-predicates.md +53 -0
- pyrsql-0.0.2/docs/usage/fastapi-sqlalchemy.md +85 -0
- pyrsql-0.0.2/docs/usage/fastapi.md +86 -0
- pyrsql-0.0.2/docs/usage/field-policies.md +46 -0
- pyrsql-0.0.2/docs/usage/join-hints.md +20 -0
- pyrsql-0.0.2/docs/usage/json.md +87 -0
- pyrsql-0.0.2/docs/usage/page.md +31 -0
- pyrsql-0.0.2/docs/usage/query.md +125 -0
- pyrsql-0.0.2/docs/usage/sort.md +42 -0
- pyrsql-0.0.2/docs/usage/value-conversion.md +44 -0
- pyrsql-0.0.2/pyproject.toml +225 -0
- pyrsql-0.0.2/src/pyrsql/__init__.py +55 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/adapters/__init__.py +1 -1
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/adapters/fastapi/__init__.py +2 -2
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/adapters/fastapi/config.py +62 -8
- pyrsql-0.0.2/src/pyrsql/adapters/fastapi/criteria.py +94 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/adapters/fastapi/dependency.py +64 -22
- pyrsql-0.0.2/src/pyrsql/adapters/fastapi/errors.py +218 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/api.py +15 -14
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/__init__.py +7 -1
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/compiler.py +58 -12
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/conversion.py +153 -47
- pyrsql-0.0.2/src/pyrsql/core/custom.py +22 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/field_policy.py +18 -4
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/json/__init__.py +7 -1
- pyrsql-0.0.2/src/pyrsql/core/json/options.py +107 -0
- pyrsql-0.0.2/src/pyrsql/core/json/path.py +85 -0
- pyrsql-0.0.2/src/pyrsql/core/json/query.py +68 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/json/values.py +37 -16
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/options.py +275 -143
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/page.py +51 -14
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/procedure_policy.py +37 -9
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/query.py +47 -28
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/sort.py +47 -26
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/integrations/__init__.py +6 -1
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/integrations/fastapi/__init__.py +6 -1
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/__init__.py +11 -0
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/examples.py +114 -0
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/helpers.py +83 -0
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/integration.py +409 -0
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/payloads.py +29 -0
- pyrsql-0.0.2/src/pyrsql/integrations/fastapi/sqlalchemy/resource.py +326 -0
- pyrsql-0.0.2/src/pyrsql/ir/__init__.py +28 -0
- pyrsql-0.0.2/src/pyrsql/ir/page.py +40 -0
- pyrsql-0.0.2/src/pyrsql/ir/query.py +181 -0
- pyrsql-0.0.2/src/pyrsql/ir/sort.py +50 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/base.py +17 -8
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/coercion.py +27 -10
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/compiled.py +19 -9
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/compiled_page.py +18 -10
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/compiled_sort.py +22 -10
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/custom.py +12 -5
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/errors.py +1 -1
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/introspection.py +77 -30
- pyrsql-0.0.2/src/pyrsql/orms/sqlalchemy/json_path.py +655 -0
- pyrsql-0.0.2/src/pyrsql/orms/sqlalchemy/orm.py +98 -0
- pyrsql-0.0.2/src/pyrsql/orms/sqlalchemy/resolver.py +251 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/sorter.py +112 -57
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/statement.py +28 -5
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/translator.py +268 -195
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/type_inference.py +10 -2
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/types.py +26 -10
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/parsing/__init__.py +6 -1
- pyrsql-0.0.2/src/pyrsql/parsing/ast.py +123 -0
- pyrsql-0.0.2/src/pyrsql/parsing/diagnostics.py +29 -0
- pyrsql-0.0.2/src/pyrsql/parsing/errors.py +47 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/parsing/lexer.py +112 -44
- pyrsql-0.0.2/src/pyrsql/parsing/limits.py +36 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/parsing/operators.py +58 -10
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/parsing/parser.py +139 -37
- pyrsql-0.0.2/src/pyrsql/parsing/source.py +83 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/parsing/tokens.py +8 -4
- pyrsql-0.0.2/src/pyrsql/selector/__init__.py +25 -0
- pyrsql-0.0.2/src/pyrsql/selector/ast.py +86 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/selector/parser.py +70 -22
- pyrsql-0.0.2/src/pyrsql/semantic/__init__.py +21 -0
- pyrsql-0.0.2/src/pyrsql/semantic/binder.py +237 -0
- pyrsql-0.0.2/src/pyrsql/semantic/diagnostics.py +29 -0
- pyrsql-0.0.2/src/pyrsql/semantic/errors.py +69 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/sorting/__init__.py +6 -2
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/sorting/ast.py +22 -7
- pyrsql-0.0.2/src/pyrsql/sorting/binder.py +184 -0
- pyrsql-0.0.2/src/pyrsql/sorting/diagnostics.py +18 -0
- pyrsql-0.0.2/src/pyrsql/sorting/errors.py +66 -0
- pyrsql-0.0.2/src/pyrsql/sorting/limits.py +27 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/sorting/parser.py +68 -21
- pyrsql-0.0.2/tests/conftest.py +1 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/functional/fastapi_adapter/test_dependency.py +81 -7
- pyrsql-0.0.2/tests/functional/fastapi_sqlalchemy/conftest.py +24 -0
- pyrsql-0.0.2/tests/functional/fastapi_sqlalchemy/test_integration.py +249 -0
- pyrsql-0.0.2/tests/functional/test_public_api.py +175 -0
- pyrsql-0.0.2/tests/integration/sqlalchemy/conftest.py +122 -0
- pyrsql-0.0.2/tests/integration/sqlalchemy/test_page_pipeline.py +37 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/integration/sqlalchemy/test_query_pipeline.py +120 -201
- pyrsql-0.0.2/tests/integration/sqlalchemy/test_sort_pipeline.py +185 -0
- pyrsql-0.0.2/tests/performance/conftest.py +67 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/test_conversion_bench.py +7 -7
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/test_json_bench.py +7 -7
- pyrsql-0.0.2/tests/performance/test_json_path_builder_bench.py +72 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/test_parser_bench.py +4 -8
- pyrsql-0.0.2/tests/performance/test_path_resolver_bench.py +70 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/test_selector_bench.py +3 -3
- pyrsql-0.0.2/tests/performance/test_semantic_bench.py +35 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/test_sqlalchemy_translation_bench.py +17 -30
- pyrsql-0.0.2/tests/unit/__init__.py +1 -0
- pyrsql-0.0.2/tests/unit/adapters/__init__.py +1 -0
- pyrsql-0.0.2/tests/unit/adapters/fastapi_adapter/conftest.py +70 -0
- pyrsql-0.0.2/tests/unit/adapters/fastapi_adapter/test_config.py +109 -0
- pyrsql-0.0.2/tests/unit/adapters/fastapi_adapter/test_criteria.py +143 -0
- pyrsql-0.0.2/tests/unit/conftest.py +12 -0
- pyrsql-0.0.2/tests/unit/core/conftest.py +97 -0
- pyrsql-0.0.2/tests/unit/core/json/test_options.py +98 -0
- pyrsql-0.0.2/tests/unit/core/json/test_path.py +42 -0
- pyrsql-0.0.2/tests/unit/core/json/test_query.py +82 -0
- pyrsql-0.0.2/tests/unit/core/json/test_values.py +31 -0
- pyrsql-0.0.2/tests/unit/core/test_conversion.py +169 -0
- pyrsql-0.0.2/tests/unit/core/test_options.py +245 -0
- pyrsql-0.0.2/tests/unit/core/test_page.py +91 -0
- pyrsql-0.0.2/tests/unit/core/test_query.py +103 -0
- pyrsql-0.0.2/tests/unit/core/test_sort.py +89 -0
- pyrsql-0.0.2/tests/unit/integrations/fastapi_sqlalchemy/conftest.py +82 -0
- pyrsql-0.0.2/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +481 -0
- pyrsql-0.0.2/tests/unit/ir/__init__.py +1 -0
- pyrsql-0.0.2/tests/unit/ir/test_page.py +32 -0
- pyrsql-0.0.2/tests/unit/ir/test_query.py +124 -0
- pyrsql-0.0.2/tests/unit/ir/test_sort.py +72 -0
- pyrsql-0.0.2/tests/unit/orms/sqlalchemy/conftest.py +83 -0
- pyrsql-0.0.2/tests/unit/orms/sqlalchemy/test_coercion.py +81 -0
- pyrsql-0.0.2/tests/unit/orms/sqlalchemy/test_json_path_builder.py +97 -0
- pyrsql-0.0.2/tests/unit/orms/sqlalchemy/test_orm.py +26 -0
- pyrsql-0.0.2/tests/unit/orms/sqlalchemy/test_resolver.py +223 -0
- pyrsql-0.0.2/tests/unit/parsing/test_lexer.py +136 -0
- pyrsql-0.0.2/tests/unit/parsing/test_parser.py +174 -0
- pyrsql-0.0.2/tests/unit/selector/test_parser.py +124 -0
- pyrsql-0.0.2/tests/unit/semantic/test_binder.py +192 -0
- pyrsql-0.0.2/tests/unit/sorting/test_binder.py +157 -0
- pyrsql-0.0.2/tests/unit/sorting/test_parser.py +136 -0
- pyrsql-0.0.1/PKG-INFO +0 -72
- pyrsql-0.0.1/README.md +0 -43
- pyrsql-0.0.1/docs/architecture.md +0 -461
- pyrsql-0.0.1/docs/pyguide.md +0 -3709
- pyrsql-0.0.1/docs/quality.md +0 -94
- pyrsql-0.0.1/docs/reference.md +0 -199
- pyrsql-0.0.1/docs/testing.md +0 -228
- pyrsql-0.0.1/docs/usage.md +0 -631
- pyrsql-0.0.1/pyproject.toml +0 -117
- pyrsql-0.0.1/src/pyrsql/__init__.py +0 -63
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/criteria.py +0 -56
- pyrsql-0.0.1/src/pyrsql/adapters/fastapi/errors.py +0 -107
- pyrsql-0.0.1/src/pyrsql/core/custom.py +0 -14
- pyrsql-0.0.1/src/pyrsql/core/json/options.py +0 -31
- pyrsql-0.0.1/src/pyrsql/core/json/path.py +0 -35
- pyrsql-0.0.1/src/pyrsql/core/json/query.py +0 -39
- pyrsql-0.0.1/src/pyrsql/integrations/fastapi/sqlalchemy.py +0 -251
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/json_path.py +0 -351
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/orm.py +0 -65
- pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/resolver.py +0 -202
- pyrsql-0.0.1/src/pyrsql/parsing/ast.py +0 -61
- pyrsql-0.0.1/src/pyrsql/parsing/errors.py +0 -25
- pyrsql-0.0.1/src/pyrsql/parsing/limits.py +0 -15
- pyrsql-0.0.1/src/pyrsql/parsing/source.py +0 -48
- pyrsql-0.0.1/src/pyrsql/selector/__init__.py +0 -30
- pyrsql-0.0.1/src/pyrsql/selector/analyzer.py +0 -58
- pyrsql-0.0.1/src/pyrsql/selector/ast.py +0 -31
- pyrsql-0.0.1/src/pyrsql/selector/semantic.py +0 -34
- pyrsql-0.0.1/src/pyrsql/semantic/__init__.py +0 -23
- pyrsql-0.0.1/src/pyrsql/semantic/analyzer.py +0 -116
- pyrsql-0.0.1/src/pyrsql/semantic/ast.py +0 -35
- pyrsql-0.0.1/src/pyrsql/semantic/errors.py +0 -40
- pyrsql-0.0.1/src/pyrsql/sorting/analyzer.py +0 -74
- pyrsql-0.0.1/src/pyrsql/sorting/errors.py +0 -21
- pyrsql-0.0.1/src/pyrsql/sorting/limits.py +0 -15
- pyrsql-0.0.1/src/pyrsql/sorting/semantic.py +0 -15
- pyrsql-0.0.1/tests/conftest.py +0 -31
- pyrsql-0.0.1/tests/fixtures/__init__.py +0 -1
- pyrsql-0.0.1/tests/fixtures/orms.py +0 -1
- pyrsql-0.0.1/tests/fixtures/query_samples.py +0 -1
- pyrsql-0.0.1/tests/fixtures/sqlalchemy_models.py +0 -1
- pyrsql-0.0.1/tests/functional/fastapi_sqlalchemy/test_integration.py +0 -126
- pyrsql-0.0.1/tests/functional/test_public_api.py +0 -80
- pyrsql-0.0.1/tests/integration/sqlalchemy/test_page_pipeline.py +0 -65
- pyrsql-0.0.1/tests/integration/sqlalchemy/test_sort_pipeline.py +0 -177
- pyrsql-0.0.1/tests/performance/test_semantic_bench.py +0 -36
- pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_config.py +0 -40
- pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_criteria.py +0 -83
- pyrsql-0.0.1/tests/unit/core/json/test_options.py +0 -32
- pyrsql-0.0.1/tests/unit/core/json/test_path.py +0 -20
- pyrsql-0.0.1/tests/unit/core/json/test_query.py +0 -28
- pyrsql-0.0.1/tests/unit/core/json/test_values.py +0 -21
- pyrsql-0.0.1/tests/unit/core/test_conversion.py +0 -109
- pyrsql-0.0.1/tests/unit/core/test_options.py +0 -160
- pyrsql-0.0.1/tests/unit/core/test_page.py +0 -99
- pyrsql-0.0.1/tests/unit/core/test_query.py +0 -87
- pyrsql-0.0.1/tests/unit/core/test_sort.py +0 -62
- pyrsql-0.0.1/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +0 -136
- pyrsql-0.0.1/tests/unit/orms/sqlalchemy/test_resolver.py +0 -187
- pyrsql-0.0.1/tests/unit/parsing/test_lexer.py +0 -78
- pyrsql-0.0.1/tests/unit/parsing/test_parser.py +0 -108
- pyrsql-0.0.1/tests/unit/selector/test_parser.py +0 -13
- pyrsql-0.0.1/tests/unit/semantic/test_analyzer.py +0 -93
- pyrsql-0.0.1/tests/unit/sorting/test_analyzer.py +0 -80
- pyrsql-0.0.1/tests/unit/sorting/test_parser.py +0 -81
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/core/joins.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/orms/sqlalchemy/__init__.py +4 -4
- {pyrsql-0.0.1 → pyrsql-0.0.2}/src/pyrsql/py.typed +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/functional/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/functional/fastapi_adapter/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/functional/fastapi_sqlalchemy/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/integration/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/integration/sqlalchemy/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/performance/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/adapters/fastapi_adapter/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/core/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/core/json/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/integrations/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/integrations/fastapi/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/integrations/fastapi_sqlalchemy/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/orms/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/orms/sqlalchemy/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/parsing/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/selector/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/semantic/__init__.py +0 -0
- {pyrsql-0.0.1 → pyrsql-0.0.2}/tests/unit/sorting/__init__.py +0 -0
pyrsql-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrsql
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Compiler-oriented RSQL query engine for safe, typed, and extensible Python ORM-backed APIs.
|
|
5
|
+
Keywords: rsql,orm,filter,pagination,sort,sqlalchemy,fastapi,rest
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Environment :: Web Environment
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Information Technology
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Framework :: FastAPI
|
|
18
|
+
Classifier: Topic :: Database
|
|
19
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Dist: ciso8601>=2.3,<3.0
|
|
23
|
+
Requires-Dist: msgspec>=0.18,<1.0
|
|
24
|
+
Requires-Dist: typing-extensions>=4.10.0 ; python_full_version < '3.11'
|
|
25
|
+
Requires-Dist: fastapi>=0.110,<1.0 ; extra == 'fastapi'
|
|
26
|
+
Requires-Dist: sqlalchemy>=2.0,<3.0 ; extra == 'sqlalchemy'
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Project-URL: Homepage, https://github.com/wskr00/pyrsql
|
|
29
|
+
Project-URL: Repository, https://github.com/wskr00/pyrsql
|
|
30
|
+
Project-URL: Issues, https://github.com/wskr00/pyrsql/issues
|
|
31
|
+
Project-URL: Documentation, https://wskr00.github.io/pyrsql/
|
|
32
|
+
Provides-Extra: fastapi
|
|
33
|
+
Provides-Extra: sqlalchemy
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# pyrsql
|
|
37
|
+
|
|
38
|
+
A compiler-oriented RSQL query engine for safe, typed, and extensible
|
|
39
|
+
filtering, sorting, and pagination in Python APIs.
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/pyrsql/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
|
|
44
|
+
pyrsql compiles RSQL query strings into ORM-specific statement objects
|
|
45
|
+
through a language frontend, semantic binder, logical IR, and pluggable
|
|
46
|
+
backend lowering - making it easy to expose complex query capabilities in
|
|
47
|
+
your API without coupling to a specific ORM or framework.
|
|
48
|
+
|
|
49
|
+
**Current backends:** SQLAlchemy 2.0
|
|
50
|
+
**Current framework adapters:** FastAPI
|
|
51
|
+
**Planned:** Django ORM, SQLModel, Flask
|
|
52
|
+
|
|
53
|
+
## Why pyrsql?
|
|
54
|
+
|
|
55
|
+
Most API filtering libraries are tightly coupled to one ORM or one framework.
|
|
56
|
+
pyrsql is built as a **compiler pipeline** - parsing, semantic analysis, and
|
|
57
|
+
IR lowering are separate stages. Adding a new ORM backend means implementing
|
|
58
|
+
one interface (`ORM`), not rewriting the parser or query language.
|
|
59
|
+
|
|
60
|
+
- **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies
|
|
61
|
+
- **Pluggable backends** - implement `compile_query` / `compile_sort` /
|
|
62
|
+
`compile_page_request` for any ORM
|
|
63
|
+
- **Pluggable framework adapters** - FastAPI today, Flask/Django tomorrow
|
|
64
|
+
- **Custom operators** - define your own RSQL operators with per-ORM lowering
|
|
65
|
+
- **Field policies** - whitelist, blacklist, aliases at global and per-model level
|
|
66
|
+
- **Type-safe** - strict mypy, Google-style docstrings, immutable value objects
|
|
67
|
+
|
|
68
|
+
## Quickstart
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install pyrsql[sqlalchemy]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import pyrsql
|
|
76
|
+
from sqlalchemy import select
|
|
77
|
+
from pyrsql.orms.sqlalchemy import SQLAlchemyORM
|
|
78
|
+
|
|
79
|
+
orm = SQLAlchemyORM()
|
|
80
|
+
stmt = select(User)
|
|
81
|
+
|
|
82
|
+
# Filter: name equals "demo", company name contains "acme"
|
|
83
|
+
stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
|
|
84
|
+
|
|
85
|
+
# Sort: by name ascending, then company name descending
|
|
86
|
+
stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
|
|
87
|
+
|
|
88
|
+
# Paginate: page 0, 25 items per page
|
|
89
|
+
stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Features
|
|
93
|
+
|
|
94
|
+
### Query (filter)
|
|
95
|
+
|
|
96
|
+
- 20+ built-in operators: `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=`, `=in=`, `=out=`, `=like=`, `=ilike=`, `=bt=`, `=na=`, `=nn=`, etc.
|
|
97
|
+
- Logical composition: `;` (AND), `,` (OR)
|
|
98
|
+
- Grouping with parentheses
|
|
99
|
+
- Wildcard matching (`*demo*`) and case-insensitive markers (`^demo`)
|
|
100
|
+
- Strict equality mode (literal `*` and `^`)
|
|
101
|
+
- Configurable `LIKE` escape character
|
|
102
|
+
- `SELECT DISTINCT` support
|
|
103
|
+
- Configurable parser limits (query length, depth, argument count)
|
|
104
|
+
|
|
105
|
+
### Sort
|
|
106
|
+
|
|
107
|
+
- Multi-field: `name,asc;company.name,desc,ic`
|
|
108
|
+
- Ignore-case modifier (`ic`)
|
|
109
|
+
- Function selectors: `@upper[name],asc`
|
|
110
|
+
|
|
111
|
+
### Pagination
|
|
112
|
+
|
|
113
|
+
- Page-number + page-size API: `PageRequest.of(0, 25)`
|
|
114
|
+
- Offset + limit API: `PageRequest.from_offset(offset=50, limit=25)`
|
|
115
|
+
- Configurable max page size
|
|
116
|
+
|
|
117
|
+
### Field Mapping & Access Control
|
|
118
|
+
|
|
119
|
+
- Aliases: `field_mapping={"username": "user.name"}`
|
|
120
|
+
- Whitelist/blacklist: `field_whitelist`, `field_blacklist`
|
|
121
|
+
- Per-model policies: `model_field_mapping`, `model_field_whitelist`, `model_field_blacklist`
|
|
122
|
+
|
|
123
|
+
### Custom Predicates
|
|
124
|
+
|
|
125
|
+
Define custom operators with ORM-specific lowering:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from pyrsql import CustomPredicateDefinition, QueryOptions
|
|
129
|
+
from pyrsql.parsing.operators import ComparisonOperator
|
|
130
|
+
|
|
131
|
+
all_match = ComparisonOperator(name="all_match", spellings=("=all=",), ...)
|
|
132
|
+
options = QueryOptions(
|
|
133
|
+
custom_predicates={
|
|
134
|
+
"all_match": CustomPredicateDefinition(operator=all_match, argument_type=str),
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Value Conversion
|
|
140
|
+
|
|
141
|
+
- Built-in: `bool`, `int`, `float`, `Decimal`, `UUID`, `date`, `time`, `datetime`, `enum`
|
|
142
|
+
- Custom converters: `ValueConverterRegistry` + `with_converter`
|
|
143
|
+
- Field-scoped: `field_value_converters={"created_at": my_converter}`
|
|
144
|
+
- Model-scoped: `model_field_value_converters={MyModel: {"field": my_converter}}`
|
|
145
|
+
|
|
146
|
+
### Join Hints
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from pyrsql import QueryOptions
|
|
150
|
+
from pyrsql.core.joins import JoinHint
|
|
151
|
+
|
|
152
|
+
options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### PostgreSQL JSON / JSONB
|
|
156
|
+
|
|
157
|
+
- **Whole-document**: direct `==`, `!=`, `=in=`, `=out=`, `=na=`, `=nn=` against JSONB columns
|
|
158
|
+
- **Nested path**: `jsonb_path_exists` via PostgreSQL `jsonpath`
|
|
159
|
+
- **Structured values**: arrays and objects passed as `jsonpath` vars
|
|
160
|
+
- **Temporal**: `JSONOptions(use_datetime=True)` for datetime-aware `jsonpath`
|
|
161
|
+
- **JSON sort**: text, integer, float, numeric, boolean, date, time, datetime
|
|
162
|
+
- **Custom function names**: `JSONOptions(path_exists_function=...)`
|
|
163
|
+
|
|
164
|
+
### FastAPI Integration
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from fastapi import Depends, FastAPI
|
|
168
|
+
from pyrsql.adapters.fastapi import criteria_dependency
|
|
169
|
+
|
|
170
|
+
app = FastAPI()
|
|
171
|
+
dependency = criteria_dependency()
|
|
172
|
+
|
|
173
|
+
@app.get("/items")
|
|
174
|
+
def list_items(criteria = Depends(dependency)):
|
|
175
|
+
...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- Auto-extracts `filter`, `sort`, `page`, `size` from query params
|
|
179
|
+
- `HTTP 422` with structured diagnostics on parse/semantic errors
|
|
180
|
+
- OpenAPI examples from configuration
|
|
181
|
+
- One-based paging support
|
|
182
|
+
- Custom query parameter names
|
|
183
|
+
|
|
184
|
+
### FastAPI + SQLAlchemy Integration
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
|
|
188
|
+
|
|
189
|
+
integration = FastAPISQLAlchemyIntegration()
|
|
190
|
+
|
|
191
|
+
@app.get("/users")
|
|
192
|
+
def list_users(stmt = Depends(integration.select_dependency(User))):
|
|
193
|
+
return {"sql": str(stmt)}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
|
|
197
|
+
- Declarative `resource()` with auto-generated OpenAPI examples
|
|
198
|
+
- `applier_dependency` for custom base statements
|
|
199
|
+
|
|
200
|
+
## Documentation
|
|
201
|
+
|
|
202
|
+
Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsql/)**.
|
|
203
|
+
|
|
204
|
+
| Section | Description |
|
|
205
|
+
|---------|-------------|
|
|
206
|
+
| [Quickstart](https://wskr00.github.io/pyrsql/quickstart/) | One-minute primer |
|
|
207
|
+
| [Usage](https://wskr00.github.io/pyrsql/usage/query/) | Filter, sort, page, JSON, FastAPI, custom predicates |
|
|
208
|
+
| [API Reference](https://wskr00.github.io/pyrsql/reference/api/) | Auto-generated from docstrings |
|
|
209
|
+
| [Operators](https://wskr00.github.io/pyrsql/reference/operators/) | Complete operator table |
|
|
210
|
+
| [Options](https://wskr00.github.io/pyrsql/reference/options/) | QueryOptions, SortOptions, JSONOptions |
|
|
211
|
+
| [Architecture](https://wskr00.github.io/pyrsql/explanation/architecture/) | Pipeline, modules, design |
|
|
212
|
+
| [Extensibility](https://wskr00.github.io/pyrsql/explanation/extensibility/) | Adding backends and adapters |
|
|
213
|
+
| [Contributing](https://wskr00.github.io/pyrsql/contributing/) | Setup, workflow, standards |
|
|
214
|
+
|
|
215
|
+
## Development Principles
|
|
216
|
+
|
|
217
|
+
- Object-oriented design
|
|
218
|
+
- SOLID principles
|
|
219
|
+
- Google Python Style Guide
|
|
220
|
+
- Strong typing
|
|
221
|
+
- ORM-neutral public API
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
[MIT](LICENSE).
|
pyrsql-0.0.2/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# pyrsql
|
|
2
|
+
|
|
3
|
+
A compiler-oriented RSQL query engine for safe, typed, and extensible
|
|
4
|
+
filtering, sorting, and pagination in Python APIs.
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/pyrsql/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
pyrsql compiles RSQL query strings into ORM-specific statement objects
|
|
10
|
+
through a language frontend, semantic binder, logical IR, and pluggable
|
|
11
|
+
backend lowering - making it easy to expose complex query capabilities in
|
|
12
|
+
your API without coupling to a specific ORM or framework.
|
|
13
|
+
|
|
14
|
+
**Current backends:** SQLAlchemy 2.0
|
|
15
|
+
**Current framework adapters:** FastAPI
|
|
16
|
+
**Planned:** Django ORM, SQLModel, Flask
|
|
17
|
+
|
|
18
|
+
## Why pyrsql?
|
|
19
|
+
|
|
20
|
+
Most API filtering libraries are tightly coupled to one ORM or one framework.
|
|
21
|
+
pyrsql is built as a **compiler pipeline** - parsing, semantic analysis, and
|
|
22
|
+
IR lowering are separate stages. Adding a new ORM backend means implementing
|
|
23
|
+
one interface (`ORM`), not rewriting the parser or query language.
|
|
24
|
+
|
|
25
|
+
- **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies
|
|
26
|
+
- **Pluggable backends** - implement `compile_query` / `compile_sort` /
|
|
27
|
+
`compile_page_request` for any ORM
|
|
28
|
+
- **Pluggable framework adapters** - FastAPI today, Flask/Django tomorrow
|
|
29
|
+
- **Custom operators** - define your own RSQL operators with per-ORM lowering
|
|
30
|
+
- **Field policies** - whitelist, blacklist, aliases at global and per-model level
|
|
31
|
+
- **Type-safe** - strict mypy, Google-style docstrings, immutable value objects
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install pyrsql[sqlalchemy]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pyrsql
|
|
41
|
+
from sqlalchemy import select
|
|
42
|
+
from pyrsql.orms.sqlalchemy import SQLAlchemyORM
|
|
43
|
+
|
|
44
|
+
orm = SQLAlchemyORM()
|
|
45
|
+
stmt = select(User)
|
|
46
|
+
|
|
47
|
+
# Filter: name equals "demo", company name contains "acme"
|
|
48
|
+
stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
|
|
49
|
+
|
|
50
|
+
# Sort: by name ascending, then company name descending
|
|
51
|
+
stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
|
|
52
|
+
|
|
53
|
+
# Paginate: page 0, 25 items per page
|
|
54
|
+
stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
### Query (filter)
|
|
60
|
+
|
|
61
|
+
- 20+ built-in operators: `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=`, `=in=`, `=out=`, `=like=`, `=ilike=`, `=bt=`, `=na=`, `=nn=`, etc.
|
|
62
|
+
- Logical composition: `;` (AND), `,` (OR)
|
|
63
|
+
- Grouping with parentheses
|
|
64
|
+
- Wildcard matching (`*demo*`) and case-insensitive markers (`^demo`)
|
|
65
|
+
- Strict equality mode (literal `*` and `^`)
|
|
66
|
+
- Configurable `LIKE` escape character
|
|
67
|
+
- `SELECT DISTINCT` support
|
|
68
|
+
- Configurable parser limits (query length, depth, argument count)
|
|
69
|
+
|
|
70
|
+
### Sort
|
|
71
|
+
|
|
72
|
+
- Multi-field: `name,asc;company.name,desc,ic`
|
|
73
|
+
- Ignore-case modifier (`ic`)
|
|
74
|
+
- Function selectors: `@upper[name],asc`
|
|
75
|
+
|
|
76
|
+
### Pagination
|
|
77
|
+
|
|
78
|
+
- Page-number + page-size API: `PageRequest.of(0, 25)`
|
|
79
|
+
- Offset + limit API: `PageRequest.from_offset(offset=50, limit=25)`
|
|
80
|
+
- Configurable max page size
|
|
81
|
+
|
|
82
|
+
### Field Mapping & Access Control
|
|
83
|
+
|
|
84
|
+
- Aliases: `field_mapping={"username": "user.name"}`
|
|
85
|
+
- Whitelist/blacklist: `field_whitelist`, `field_blacklist`
|
|
86
|
+
- Per-model policies: `model_field_mapping`, `model_field_whitelist`, `model_field_blacklist`
|
|
87
|
+
|
|
88
|
+
### Custom Predicates
|
|
89
|
+
|
|
90
|
+
Define custom operators with ORM-specific lowering:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from pyrsql import CustomPredicateDefinition, QueryOptions
|
|
94
|
+
from pyrsql.parsing.operators import ComparisonOperator
|
|
95
|
+
|
|
96
|
+
all_match = ComparisonOperator(name="all_match", spellings=("=all=",), ...)
|
|
97
|
+
options = QueryOptions(
|
|
98
|
+
custom_predicates={
|
|
99
|
+
"all_match": CustomPredicateDefinition(operator=all_match, argument_type=str),
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Value Conversion
|
|
105
|
+
|
|
106
|
+
- Built-in: `bool`, `int`, `float`, `Decimal`, `UUID`, `date`, `time`, `datetime`, `enum`
|
|
107
|
+
- Custom converters: `ValueConverterRegistry` + `with_converter`
|
|
108
|
+
- Field-scoped: `field_value_converters={"created_at": my_converter}`
|
|
109
|
+
- Model-scoped: `model_field_value_converters={MyModel: {"field": my_converter}}`
|
|
110
|
+
|
|
111
|
+
### Join Hints
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pyrsql import QueryOptions
|
|
115
|
+
from pyrsql.core.joins import JoinHint
|
|
116
|
+
|
|
117
|
+
options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### PostgreSQL JSON / JSONB
|
|
121
|
+
|
|
122
|
+
- **Whole-document**: direct `==`, `!=`, `=in=`, `=out=`, `=na=`, `=nn=` against JSONB columns
|
|
123
|
+
- **Nested path**: `jsonb_path_exists` via PostgreSQL `jsonpath`
|
|
124
|
+
- **Structured values**: arrays and objects passed as `jsonpath` vars
|
|
125
|
+
- **Temporal**: `JSONOptions(use_datetime=True)` for datetime-aware `jsonpath`
|
|
126
|
+
- **JSON sort**: text, integer, float, numeric, boolean, date, time, datetime
|
|
127
|
+
- **Custom function names**: `JSONOptions(path_exists_function=...)`
|
|
128
|
+
|
|
129
|
+
### FastAPI Integration
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from fastapi import Depends, FastAPI
|
|
133
|
+
from pyrsql.adapters.fastapi import criteria_dependency
|
|
134
|
+
|
|
135
|
+
app = FastAPI()
|
|
136
|
+
dependency = criteria_dependency()
|
|
137
|
+
|
|
138
|
+
@app.get("/items")
|
|
139
|
+
def list_items(criteria = Depends(dependency)):
|
|
140
|
+
...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- Auto-extracts `filter`, `sort`, `page`, `size` from query params
|
|
144
|
+
- `HTTP 422` with structured diagnostics on parse/semantic errors
|
|
145
|
+
- OpenAPI examples from configuration
|
|
146
|
+
- One-based paging support
|
|
147
|
+
- Custom query parameter names
|
|
148
|
+
|
|
149
|
+
### FastAPI + SQLAlchemy Integration
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
|
|
153
|
+
|
|
154
|
+
integration = FastAPISQLAlchemyIntegration()
|
|
155
|
+
|
|
156
|
+
@app.get("/users")
|
|
157
|
+
def list_users(stmt = Depends(integration.select_dependency(User))):
|
|
158
|
+
return {"sql": str(stmt)}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
|
|
162
|
+
- Declarative `resource()` with auto-generated OpenAPI examples
|
|
163
|
+
- `applier_dependency` for custom base statements
|
|
164
|
+
|
|
165
|
+
## Documentation
|
|
166
|
+
|
|
167
|
+
Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsql/)**.
|
|
168
|
+
|
|
169
|
+
| Section | Description |
|
|
170
|
+
|---------|-------------|
|
|
171
|
+
| [Quickstart](https://wskr00.github.io/pyrsql/quickstart/) | One-minute primer |
|
|
172
|
+
| [Usage](https://wskr00.github.io/pyrsql/usage/query/) | Filter, sort, page, JSON, FastAPI, custom predicates |
|
|
173
|
+
| [API Reference](https://wskr00.github.io/pyrsql/reference/api/) | Auto-generated from docstrings |
|
|
174
|
+
| [Operators](https://wskr00.github.io/pyrsql/reference/operators/) | Complete operator table |
|
|
175
|
+
| [Options](https://wskr00.github.io/pyrsql/reference/options/) | QueryOptions, SortOptions, JSONOptions |
|
|
176
|
+
| [Architecture](https://wskr00.github.io/pyrsql/explanation/architecture/) | Pipeline, modules, design |
|
|
177
|
+
| [Extensibility](https://wskr00.github.io/pyrsql/explanation/extensibility/) | Adding backends and adapters |
|
|
178
|
+
| [Contributing](https://wskr00.github.io/pyrsql/contributing/) | Setup, workflow, standards |
|
|
179
|
+
|
|
180
|
+
## Development Principles
|
|
181
|
+
|
|
182
|
+
- Object-oriented design
|
|
183
|
+
- SOLID principles
|
|
184
|
+
- Google Python Style Guide
|
|
185
|
+
- Strong typing
|
|
186
|
+
- ORM-neutral public API
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/wskr00/pyrsql.git
|
|
7
|
+
cd pyrsql
|
|
8
|
+
uv sync --all-extras
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Development workflow
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv run ruff check . # lint
|
|
15
|
+
uv run ruff format . # format
|
|
16
|
+
uv run mypy # type check
|
|
17
|
+
uv run pytest # tests
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Project standards
|
|
21
|
+
|
|
22
|
+
- Google Python Style Guide
|
|
23
|
+
- strict mypy type checking
|
|
24
|
+
- ruff for linting and formatting
|
|
25
|
+
- pytest with `unit`, `integration`, `functional`, `performance` layers
|
|
26
|
+
- ORM-neutral core - zero ORM imports in `pyrsql.core`, `pyrsql.ir`, `pyrsql.parsing`, `pyrsql.semantic`
|
|
27
|
+
|
|
28
|
+
## Test layers
|
|
29
|
+
|
|
30
|
+
| Layer | Purpose | Run with |
|
|
31
|
+
|-------|---------|----------|
|
|
32
|
+
| `unit` | Isolated module/class tests | `pytest tests/unit/` |
|
|
33
|
+
| `integration` | Cross-component pipelines | `pytest tests/integration/` |
|
|
34
|
+
| `functional` | Public API behavior | `pytest tests/functional/` |
|
|
35
|
+
| `performance` | Hotspot benchmarks | `pytest tests/performance/` |
|
|
36
|
+
|
|
37
|
+
## Docs
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv run mkdocs serve # live preview at http://localhost:8000
|
|
41
|
+
uv run mkdocs build # build static site
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Pull requests
|
|
45
|
+
|
|
46
|
+
- Keep PRs focused - one feature or fix per PR
|
|
47
|
+
- Update docs if the public API changes
|
|
48
|
+
- Run `ruff check . && mypy && pytest` before pushing
|
|
49
|
+
- Performance PRs should include benchmark results
|