pyrsql 0.0.3__tar.gz → 0.0.4__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 (198) hide show
  1. {pyrsql-0.0.3 → pyrsql-0.0.4}/PKG-INFO +51 -10
  2. {pyrsql-0.0.3 → pyrsql-0.0.4}/README.md +50 -9
  3. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/explanation/architecture.md +3 -4
  4. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/explanation/extensibility.md +9 -17
  5. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/index.md +27 -9
  6. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/quickstart.md +39 -1
  7. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/reference/api.md +1 -2
  8. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/testing.md +0 -1
  9. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/fastapi-sqlalchemy.md +20 -4
  10. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/fastapi.md +23 -0
  11. {pyrsql-0.0.3 → pyrsql-0.0.4}/pyproject.toml +1 -1
  12. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/__init__.py +0 -6
  13. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/adapters/fastapi/__init__.py +5 -1
  14. pyrsql-0.0.4/src/pyrsql/adapters/fastapi/config.py +84 -0
  15. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/adapters/fastapi/criteria.py +0 -2
  16. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/adapters/fastapi/dependency.py +130 -53
  17. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/adapters/fastapi/errors.py +9 -14
  18. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/api.py +3 -3
  19. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/__init__.py +0 -8
  20. pyrsql-0.0.4/src/pyrsql/core/compiler.py +15 -0
  21. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/conversion.py +2 -3
  22. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/options.py +41 -153
  23. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/page.py +4 -9
  24. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/query.py +4 -8
  25. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/sort.py +4 -8
  26. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/source_issues.py +1 -1
  27. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/helpers.py +4 -16
  28. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/integration.py +21 -103
  29. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/resource.py +10 -80
  30. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/__init__.py +0 -6
  31. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/base.py +5 -19
  32. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/compiled_page.py +3 -4
  33. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/compiled_query.py +1 -1
  34. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/compiled_sort.py +1 -1
  35. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/introspection.py +4 -16
  36. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/json_path.py +6 -9
  37. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/orm.py +10 -33
  38. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/resolver.py +1 -17
  39. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/sorter.py +7 -15
  40. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/translator.py +3 -16
  41. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/type_inference.py +5 -8
  42. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/types.py +0 -2
  43. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/__init__.py +1 -4
  44. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/ast.py +4 -20
  45. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/errors.py +0 -2
  46. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/lexer.py +7 -29
  47. pyrsql-0.0.4/src/pyrsql/parsing/limits.py +19 -0
  48. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/operators.py +1 -92
  49. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/parser.py +8 -15
  50. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/source.py +0 -23
  51. pyrsql-0.0.4/src/pyrsql/selector/ast.py +31 -0
  52. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/selector/parser.py +34 -72
  53. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/semantic/binder.py +0 -12
  54. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/binder.py +0 -6
  55. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/errors.py +1 -1
  56. pyrsql-0.0.4/src/pyrsql/sorting/limits.py +16 -0
  57. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/parser.py +3 -36
  58. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_adapter/test_dependency.py +27 -0
  59. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/test_public_api.py +3 -7
  60. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_hpp.py +21 -1
  61. pyrsql-0.0.4/tests/unit/adapters/fastapi_adapter/test_config.py +39 -0
  62. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/adapters/fastapi_adapter/test_criteria.py +4 -13
  63. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/conftest.py +0 -5
  64. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_options.py +2 -0
  65. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_page.py +3 -3
  66. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_query.py +3 -3
  67. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_sort.py +3 -3
  68. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +0 -182
  69. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/parsing/test_lexer.py +0 -32
  70. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/parsing/test_parser.py +0 -72
  71. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/parsing/test_source.py +1 -6
  72. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/selector/test_parser.py +10 -88
  73. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/sorting/test_parser.py +0 -62
  74. pyrsql-0.0.3/src/pyrsql/adapters/fastapi/config.py +0 -169
  75. pyrsql-0.0.3/src/pyrsql/core/compiler.py +0 -45
  76. pyrsql-0.0.3/src/pyrsql/parsing/diagnostics.py +0 -9
  77. pyrsql-0.0.3/src/pyrsql/parsing/limits.py +0 -48
  78. pyrsql-0.0.3/src/pyrsql/parsing/normalization.py +0 -51
  79. pyrsql-0.0.3/src/pyrsql/selector/ast.py +0 -45
  80. pyrsql-0.0.3/src/pyrsql/sorting/limits.py +0 -33
  81. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/test_config.py +0 -111
  82. pyrsql-0.0.3/tests/unit/parsing/test_operators.py +0 -105
  83. pyrsql-0.0.3/tests/unit/selector/test_ast.py +0 -30
  84. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/contributing.md +0 -0
  85. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/reference/operators.md +0 -0
  86. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/reference/options.md +0 -0
  87. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/async.md +0 -0
  88. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/custom-predicates.md +0 -0
  89. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/errors.md +0 -0
  90. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/field-policies.md +0 -0
  91. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/join-hints.md +0 -0
  92. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/json.md +0 -0
  93. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/page.md +0 -0
  94. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/query.md +0 -0
  95. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/sort.md +0 -0
  96. {pyrsql-0.0.3 → pyrsql-0.0.4}/docs/usage/value-conversion.md +0 -0
  97. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/adapters/__init__.py +0 -0
  98. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/binding_policy.py +0 -0
  99. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/custom.py +0 -0
  100. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/field_policy.py +0 -0
  101. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/joins.py +0 -0
  102. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/json/__init__.py +0 -0
  103. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/json/options.py +0 -0
  104. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/json/path.py +0 -0
  105. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/json/query.py +0 -0
  106. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/json/values.py +0 -0
  107. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/procedure_policy.py +0 -0
  108. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/core/validation.py +0 -0
  109. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/__init__.py +0 -0
  110. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/__init__.py +0 -0
  111. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/__init__.py +0 -0
  112. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/examples.py +0 -0
  113. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/integrations/fastapi/sqlalchemy/payloads.py +0 -0
  114. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/__init__.py +0 -0
  115. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/coercion.py +0 -0
  116. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/custom.py +0 -0
  117. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/errors.py +0 -0
  118. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/orms/sqlalchemy/statement.py +0 -0
  119. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/parsing/tokens.py +0 -0
  120. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/py.typed +0 -0
  121. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/selector/__init__.py +0 -0
  122. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/semantic/__init__.py +0 -0
  123. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/semantic/diagnostics.py +0 -0
  124. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/semantic/errors.py +0 -0
  125. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/__init__.py +0 -0
  126. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/ast.py +0 -0
  127. {pyrsql-0.0.3 → pyrsql-0.0.4}/src/pyrsql/sorting/diagnostics.py +0 -0
  128. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/__init__.py +0 -0
  129. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/conftest.py +0 -0
  130. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/__init__.py +0 -0
  131. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_adapter/__init__.py +0 -0
  132. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_adapter/test_async_dependency.py +0 -0
  133. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_sqlalchemy/__init__.py +0 -0
  134. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_sqlalchemy/conftest.py +0 -0
  135. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_sqlalchemy/test_async_integration.py +0 -0
  136. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/functional/fastapi_sqlalchemy/test_integration.py +0 -0
  137. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/__init__.py +0 -0
  138. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/__init__.py +0 -0
  139. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/conftest.py +0 -0
  140. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/test_async_pipeline.py +0 -0
  141. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/test_page_pipeline.py +0 -0
  142. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/test_query_pipeline.py +0 -0
  143. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/integration/sqlalchemy/test_sort_pipeline.py +0 -0
  144. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/__init__.py +0 -0
  145. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/conftest.py +0 -0
  146. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_conversion_bench.py +0 -0
  147. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_json_bench.py +0 -0
  148. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_json_path_builder_bench.py +0 -0
  149. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_parser_bench.py +0 -0
  150. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_path_resolver_bench.py +0 -0
  151. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_selector_bench.py +0 -0
  152. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_semantic_bench.py +0 -0
  153. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/performance/test_sqlalchemy_translation_bench.py +0 -0
  154. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/__init__.py +0 -0
  155. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/conftest.py +0 -0
  156. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_configuration_hardening.py +0 -0
  157. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_error_sanitization.py +0 -0
  158. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_injection.py +0 -0
  159. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_limits.py +0 -0
  160. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_policies.py +0 -0
  161. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_fastapi_sqlalchemy_resource_security.py +0 -0
  162. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_sqlalchemy_injection.py +0 -0
  163. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/security/test_sqlalchemy_jsonpath_security.py +0 -0
  164. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/__init__.py +0 -0
  165. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/adapters/__init__.py +0 -0
  166. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/adapters/fastapi_adapter/__init__.py +0 -0
  167. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/adapters/fastapi_adapter/conftest.py +0 -0
  168. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/adapters/fastapi_adapter/test_errors.py +0 -0
  169. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/conftest.py +0 -0
  170. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/__init__.py +0 -0
  171. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/json/__init__.py +0 -0
  172. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/json/test_options.py +0 -0
  173. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/json/test_path.py +0 -0
  174. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/json/test_query.py +0 -0
  175. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/json/test_values.py +0 -0
  176. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_conversion.py +0 -0
  177. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/core/test_custom.py +0 -0
  178. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/__init__.py +0 -0
  179. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/fastapi/__init__.py +0 -0
  180. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/fastapi_sqlalchemy/__init__.py +0 -0
  181. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/fastapi_sqlalchemy/conftest.py +0 -0
  182. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/integrations/fastapi_sqlalchemy/test_examples.py +0 -0
  183. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/__init__.py +0 -0
  184. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/__init__.py +0 -0
  185. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/conftest.py +0 -0
  186. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/test_coercion.py +0 -0
  187. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/test_json_path_builder.py +0 -0
  188. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/test_orm.py +0 -0
  189. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/test_resolver.py +0 -0
  190. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/orms/sqlalchemy/test_statement.py +0 -0
  191. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/parsing/__init__.py +0 -0
  192. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/parsing/test_ast.py +0 -0
  193. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/selector/__init__.py +0 -0
  194. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/semantic/__init__.py +0 -0
  195. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/semantic/test_binder.py +0 -0
  196. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/sorting/__init__.py +0 -0
  197. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/sorting/test_ast.py +0 -0
  198. {pyrsql-0.0.3 → pyrsql-0.0.4}/tests/unit/sorting/test_binder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrsql
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Compiler-oriented RSQL query engine for safe, typed, and extensible Python ORM-backed APIs.
5
5
  Keywords: rsql,orm,filter,pagination,sort,sqlalchemy,fastapi,rest
6
6
  License-Expression: MIT
@@ -46,6 +46,11 @@ through a language frontend, semantic binding, and pluggable backend
46
46
  lowering - making it easy to expose complex query capabilities in your API
47
47
  without coupling to a specific ORM or framework.
48
48
 
49
+ For FastAPI applications using SQLAlchemy, the recommended entry point is
50
+ `FastAPISQLAlchemyIntegration`. It provides route-ready dependencies and
51
+ declarative resources; the lower-level query, adapter, and ORM APIs remain
52
+ available when more control is needed.
53
+
49
54
  **Current backends:** SQLAlchemy 2.0
50
55
  **Current framework adapters:** FastAPI
51
56
  **Planned:** Django ORM, SQLModel, Flask
@@ -73,9 +78,41 @@ language.
73
78
  ## Quickstart
74
79
 
75
80
  ```bash
76
- pip install pyrsql[sqlalchemy]
81
+ pip install pyrsql[fastapi,sqlalchemy]
77
82
  ```
78
83
 
84
+ ```python
85
+ from typing import Annotated, Any
86
+
87
+ from fastapi import Depends, FastAPI
88
+
89
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
90
+
91
+ app = FastAPI()
92
+ integration = FastAPISQLAlchemyIntegration()
93
+ users = integration.resource(
94
+ User,
95
+ filterable_fields={"id", "name"},
96
+ sortable_fields={"name"},
97
+ default_sort="name,asc",
98
+ max_page_size=100,
99
+ )
100
+
101
+ @app.get("/users")
102
+ def list_users(
103
+ stmt: Annotated[Any, Depends(users.select_dependency())],
104
+ ):
105
+ return {"sql": str(stmt)}
106
+ ```
107
+
108
+ Clients can now use `filter`, `sort`, `page`, and `size` query parameters.
109
+ See [FastAPI + SQLAlchemy](https://wskr00.github.io/pyrsql/usage/fastapi-sqlalchemy/)
110
+ for count, pagination, custom base statements, error behavior, and async use.
111
+
112
+ ### Lower-level SQLAlchemy API
113
+
114
+ Use the direct API when you do not need a FastAPI route dependency:
115
+
79
116
  ```python
80
117
  import pyrsql
81
118
  from sqlalchemy import select
@@ -84,13 +121,13 @@ from pyrsql.orms.sqlalchemy import SQLAlchemyORM
84
121
  orm = SQLAlchemyORM()
85
122
  stmt = select(User)
86
123
 
87
- # Filter: name equals "demo", company name contains "acme"
88
- stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
124
+ stmt = pyrsql.parse("name==demo;company.name==acme*").apply(
125
+ stmt,
126
+ User,
127
+ orm=orm,
128
+ )
89
129
 
90
- # Sort: by name ascending, then company name descending
91
130
  stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
92
-
93
- # Paginate: page 0, 25 items per page
94
131
  stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
95
132
  ```
96
133
 
@@ -168,6 +205,9 @@ options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
168
205
 
169
206
  ### FastAPI Integration
170
207
 
208
+ Use this adapter directly when you need only parsed `RequestCriteria`; for
209
+ SQLAlchemy routes, prefer the integration below.
210
+
171
211
  ```python
172
212
  from fastapi import Depends, FastAPI
173
213
  from pyrsql.adapters.fastapi import criteria_dependency
@@ -181,10 +221,11 @@ def list_items(criteria = Depends(dependency)):
181
221
  ```
182
222
 
183
223
  - Auto-extracts `filter`, `sort`, `page`, `size` from query params
184
- - `HTTP 422` with structured diagnostics on parse/semantic errors
224
+ - Structured `HTTP 400` parse errors and `HTTP 422` semantic errors
185
225
  - OpenAPI examples from configuration
186
226
  - One-based paging support
187
227
  - Custom query parameter names
228
+ - Optional repeated sort parameters through `SortParameterFormat.REPEATED`
188
229
 
189
230
  ### FastAPI + SQLAlchemy Integration
190
231
 
@@ -199,7 +240,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
199
240
  ```
200
241
 
201
242
  - `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
202
- - Declarative `resource()` with auto-generated OpenAPI examples
243
+ - Declarative `resource()` with auto-generated OpenAPI examples and field policies
203
244
  - `applier_dependency` for custom base statements
204
245
  - Compatible with both sync `Session` and async `AsyncSession` execution
205
246
  - FastAPI parse and page-validation failures become structured `HTTP 400` payloads
@@ -207,7 +248,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
207
248
 
208
249
  ### Concurrency and Validation
209
250
 
210
- - Shared integration and ORM metadata caches are protected for free-threaded execution
251
+ - Shared base-select and ORM metadata caches are protected for free-threaded execution
211
252
  - Async support is validated for adapter, ORM, and integration flows
212
253
  - Dedicated async, free-threaded, and security test suites validate these flows
213
254
 
@@ -11,6 +11,11 @@ through a language frontend, semantic binding, and pluggable backend
11
11
  lowering - making it easy to expose complex query capabilities in your API
12
12
  without coupling to a specific ORM or framework.
13
13
 
14
+ For FastAPI applications using SQLAlchemy, the recommended entry point is
15
+ `FastAPISQLAlchemyIntegration`. It provides route-ready dependencies and
16
+ declarative resources; the lower-level query, adapter, and ORM APIs remain
17
+ available when more control is needed.
18
+
14
19
  **Current backends:** SQLAlchemy 2.0
15
20
  **Current framework adapters:** FastAPI
16
21
  **Planned:** Django ORM, SQLModel, Flask
@@ -38,9 +43,41 @@ language.
38
43
  ## Quickstart
39
44
 
40
45
  ```bash
41
- pip install pyrsql[sqlalchemy]
46
+ pip install pyrsql[fastapi,sqlalchemy]
42
47
  ```
43
48
 
49
+ ```python
50
+ from typing import Annotated, Any
51
+
52
+ from fastapi import Depends, FastAPI
53
+
54
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
55
+
56
+ app = FastAPI()
57
+ integration = FastAPISQLAlchemyIntegration()
58
+ users = integration.resource(
59
+ User,
60
+ filterable_fields={"id", "name"},
61
+ sortable_fields={"name"},
62
+ default_sort="name,asc",
63
+ max_page_size=100,
64
+ )
65
+
66
+ @app.get("/users")
67
+ def list_users(
68
+ stmt: Annotated[Any, Depends(users.select_dependency())],
69
+ ):
70
+ return {"sql": str(stmt)}
71
+ ```
72
+
73
+ Clients can now use `filter`, `sort`, `page`, and `size` query parameters.
74
+ See [FastAPI + SQLAlchemy](https://wskr00.github.io/pyrsql/usage/fastapi-sqlalchemy/)
75
+ for count, pagination, custom base statements, error behavior, and async use.
76
+
77
+ ### Lower-level SQLAlchemy API
78
+
79
+ Use the direct API when you do not need a FastAPI route dependency:
80
+
44
81
  ```python
45
82
  import pyrsql
46
83
  from sqlalchemy import select
@@ -49,13 +86,13 @@ from pyrsql.orms.sqlalchemy import SQLAlchemyORM
49
86
  orm = SQLAlchemyORM()
50
87
  stmt = select(User)
51
88
 
52
- # Filter: name equals "demo", company name contains "acme"
53
- stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
89
+ stmt = pyrsql.parse("name==demo;company.name==acme*").apply(
90
+ stmt,
91
+ User,
92
+ orm=orm,
93
+ )
54
94
 
55
- # Sort: by name ascending, then company name descending
56
95
  stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
57
-
58
- # Paginate: page 0, 25 items per page
59
96
  stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
60
97
  ```
61
98
 
@@ -133,6 +170,9 @@ options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
133
170
 
134
171
  ### FastAPI Integration
135
172
 
173
+ Use this adapter directly when you need only parsed `RequestCriteria`; for
174
+ SQLAlchemy routes, prefer the integration below.
175
+
136
176
  ```python
137
177
  from fastapi import Depends, FastAPI
138
178
  from pyrsql.adapters.fastapi import criteria_dependency
@@ -146,10 +186,11 @@ def list_items(criteria = Depends(dependency)):
146
186
  ```
147
187
 
148
188
  - Auto-extracts `filter`, `sort`, `page`, `size` from query params
149
- - `HTTP 422` with structured diagnostics on parse/semantic errors
189
+ - Structured `HTTP 400` parse errors and `HTTP 422` semantic errors
150
190
  - OpenAPI examples from configuration
151
191
  - One-based paging support
152
192
  - Custom query parameter names
193
+ - Optional repeated sort parameters through `SortParameterFormat.REPEATED`
153
194
 
154
195
  ### FastAPI + SQLAlchemy Integration
155
196
 
@@ -164,7 +205,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
164
205
  ```
165
206
 
166
207
  - `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
167
- - Declarative `resource()` with auto-generated OpenAPI examples
208
+ - Declarative `resource()` with auto-generated OpenAPI examples and field policies
168
209
  - `applier_dependency` for custom base statements
169
210
  - Compatible with both sync `Session` and async `AsyncSession` execution
170
211
  - FastAPI parse and page-validation failures become structured `HTTP 400` payloads
@@ -172,7 +213,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
172
213
 
173
214
  ### Concurrency and Validation
174
215
 
175
- - Shared integration and ORM metadata caches are protected for free-threaded execution
216
+ - Shared base-select and ORM metadata caches are protected for free-threaded execution
176
217
  - Async support is validated for adapter, ORM, and integration flows
177
218
  - Dedicated async, free-threaded, and security test suites validate these flows
178
219
 
@@ -108,7 +108,6 @@ Owns user-facing ORM-agnostic objects:
108
108
  - `JSONPath`, `JSONPathComparison`
109
109
  - `JSONScalarNormalizer`, `JSONScalarValue`
110
110
  - `JoinHint`
111
- - `CompilationResult`, `SortCompilationResult`, `PageCompilationResult`
112
111
 
113
112
  The `core/json/` package owns JSON-aware comparison models and value
114
113
  normalization, keeping JSON semantics ORM-neutral.
@@ -185,9 +184,9 @@ Implement `pyrsql.orms.base.ORM`:
185
184
 
186
185
  ```python
187
186
  class ORM(ABC):
188
- def compile_query(self, query: Query) -> CompiledQuery: ...
189
- def compile_sort(self, sort: Sort) -> CompiledSort: ...
190
- def compile_page_request(self, page_request: PageRequest) -> CompiledPageRequest: ...
187
+ def compile_query(self, query: Query) -> CompiledArtifact: ...
188
+ def compile_sort(self, sort: Sort) -> CompiledArtifact: ...
189
+ def compile_page_request(self, page_request: PageRequest) -> CompiledArtifact: ...
191
190
  ```
192
191
 
193
192
  Each method receives one ORM-neutral core object and returns a compiled object
@@ -9,20 +9,17 @@ The core (`pyrsql.core`, `pyrsql.parsing`, `pyrsql.semantic`,
9
9
  Implement the `ORM` abstract base class:
10
10
 
11
11
  ```python
12
- from pyrsql.orms.base import ORM, CompiledPageRequest, CompiledQuery, CompiledSort
12
+ from pyrsql.core.compiler import CompiledArtifact
13
+ from pyrsql.orms.base import ORM
13
14
 
14
15
  class MyORM(ORM):
15
- @property
16
- def name(self) -> str:
17
- return "my-orm"
18
-
19
- def compile_query(self, query: Query) -> CompiledQuery:
16
+ def compile_query(self, query: Query) -> CompiledArtifact:
20
17
  ...
21
18
 
22
- def compile_sort(self, sort: Sort) -> CompiledSort:
19
+ def compile_sort(self, sort: Sort) -> CompiledArtifact:
23
20
  ...
24
21
 
25
- def compile_page_request(self, page_request: PageRequest) -> CompiledPageRequest:
22
+ def compile_page_request(self, page_request: PageRequest) -> CompiledArtifact:
26
23
  ...
27
24
  ```
28
25
 
@@ -49,23 +46,18 @@ The FastAPI adapter (`pyrsql.adapters.fastapi`) is the reference:
49
46
 
50
47
  ## Backend contract in detail
51
48
 
52
- ### `CompiledQuery`
49
+ ### `CompiledArtifact`
53
50
 
54
51
  ```python
55
- class CompiledQuery(Protocol):
52
+ class CompiledArtifact(Protocol):
56
53
  def apply(self, target: _TargetT, model: type[_ModelT]) -> _TargetT: ...
57
54
  ```
58
55
 
59
56
  Receives an ORM-specific target and model class, then returns the modified
60
57
  target with query conditions applied.
61
58
 
62
- ### `CompiledSort`
63
-
64
- Same contract as `CompiledQuery`, but applies sort ordering.
65
-
66
- ### `CompiledPageRequest`
67
-
68
- Same contract, but applies pagination semantics such as `LIMIT` and `OFFSET`.
59
+ The same contract applies to query, sort, and pagination artifacts such as
60
+ `LIMIT` and `OFFSET`.
69
61
 
70
62
  ## Integration layer
71
63
 
@@ -10,20 +10,38 @@ capabilities in your API without coupling to a specific ORM or framework.
10
10
  ## Install
11
11
 
12
12
  ```bash
13
- pip install pyrsql[sqlalchemy]
13
+ pip install pyrsql[fastapi,sqlalchemy]
14
14
  ```
15
15
 
16
- ## First query
16
+ ## Recommended: FastAPI + SQLAlchemy
17
17
 
18
18
  ```python
19
- import pyrsql
20
- from sqlalchemy import select
21
- from pyrsql.orms.sqlalchemy import SQLAlchemyORM
19
+ from typing import Annotated, Any
22
20
 
23
- orm = SQLAlchemyORM()
24
- stmt = pyrsql.parse("name==demo;age=gt=18").apply(select(User), User, orm=orm)
21
+ from fastapi import Depends, FastAPI
22
+
23
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
24
+
25
+ app = FastAPI()
26
+ integration = FastAPISQLAlchemyIntegration()
27
+ users = integration.resource(
28
+ User,
29
+ filterable_fields={"id", "name"},
30
+ sortable_fields={"name"},
31
+ default_sort="name,asc",
32
+ )
33
+
34
+ @app.get("/users")
35
+ def list_users(
36
+ stmt: Annotated[Any, Depends(users.select_dependency())],
37
+ ):
38
+ return {"sql": str(stmt)}
25
39
  ```
26
40
 
41
+ The integration parses request criteria, applies SQLAlchemy filters/sorting/
42
+ pagination, and exposes a route-ready dependency. It does not execute database
43
+ I/O.
44
+
27
45
  ## Key features
28
46
 
29
47
  - **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies
@@ -43,11 +61,11 @@ stmt = pyrsql.parse("name==demo;age=gt=18").apply(select(User), User, orm=orm)
43
61
 
44
62
  ## Integration options
45
63
 
46
- - **FastAPI adapter** - parse `filter`, `sort`, `page`, and `size` into
47
- `RequestCriteria`
48
64
  - **FastAPI + SQLAlchemy integration** - route-ready dependencies,
49
65
  declarative `resource()` helpers, count/paginated bundles, and normalized
50
66
  backend error translation
67
+ - **FastAPI adapter** - parse `filter`, `sort`, `page`, and `size` into
68
+ `RequestCriteria` when you need criteria without SQLAlchemy integration
51
69
 
52
70
  ## Next steps
53
71
 
@@ -9,6 +9,44 @@ pip install pyrsql[fastapi] # with FastAPI
9
9
  pip install pyrsql[fastapi,sqlalchemy] # both
10
10
  ```
11
11
 
12
+ ## Recommended: FastAPI + SQLAlchemy
13
+
14
+ For a FastAPI application backed by SQLAlchemy, start with the integration.
15
+ It owns the public query contract for one model and gives routes a ready-to-use
16
+ SQLAlchemy statement.
17
+
18
+ ```python
19
+ from typing import Annotated, Any
20
+
21
+ from fastapi import Depends, FastAPI
22
+
23
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
24
+
25
+ app = FastAPI()
26
+ integration = FastAPISQLAlchemyIntegration()
27
+ users = integration.resource(
28
+ User,
29
+ filterable_fields={"id", "name"},
30
+ sortable_fields={"name"},
31
+ default_sort="name,asc",
32
+ )
33
+
34
+ @app.get("/users")
35
+ def list_users(
36
+ stmt: Annotated[Any, Depends(users.select_dependency())],
37
+ ):
38
+ return {"sql": str(stmt)}
39
+ ```
40
+
41
+ Clients can use `filter`, `sort`, `page`, and `size`. The integration also
42
+ offers count and paginated dependencies. See [FastAPI + SQLAlchemy](usage/fastapi-sqlalchemy.md)
43
+ for the complete route API.
44
+
45
+ ## Building blocks
46
+
47
+ The following sections show the lower-level APIs used by the integration. They
48
+ are useful for non-FastAPI applications or when you need direct control.
49
+
12
50
  ## Filter
13
51
 
14
52
  ```python
@@ -83,7 +121,7 @@ Parse and page-validation errors become structured `HTTP 400` responses.
83
121
  Semantic and backend integration errors become structured `HTTP 422`
84
122
  responses.
85
123
 
86
- ## FastAPI + SQLAlchemy
124
+ ## Direct integration dependency
87
125
 
88
126
  ```python
89
127
  from typing import Annotated, Any
@@ -27,12 +27,11 @@
27
27
  - ValueConverterRegistry
28
28
  - JoinHint
29
29
  - ORM
30
- - CompilationResult
31
30
  - DEFAULT_JSON_OPTIONS
32
31
 
33
32
  ## Subpackages
34
33
 
35
- - `pyrsql.adapters.fastapi` - FastAPI adapter (FastAPICriteriaConfig, RequestCriteria, CriteriaDependency)
34
+ - `pyrsql.adapters.fastapi` - FastAPI adapter (FastAPICriteriaConfig, RequestCriteria, CriteriaDependency, SortParameterFormat)
36
35
  - `pyrsql.integrations.fastapi` - FastAPI + SQLAlchemy (FastAPISQLAlchemyIntegration, FastAPISQLAlchemyResource)
37
36
  - `pyrsql.orms.sqlalchemy` - SQLAlchemy backend (SQLAlchemyORM)
38
37
  - `pyrsql.core` - ORM-neutral core types and options
@@ -134,7 +134,6 @@ shared mutable state.
134
134
 
135
135
  In `pyrsql`, the main targets are:
136
136
 
137
- - integration dependency caches
138
137
  - integration base-statement caches
139
138
  - SQLAlchemy model-introspection caches
140
139
  - resolver caches
@@ -1,6 +1,6 @@
1
1
  # FastAPI + SQLAlchemy
2
2
 
3
- ## Setup
3
+ ## Recommended entry point
4
4
 
5
5
  ```python
6
6
  from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
@@ -21,6 +21,23 @@ The integration works with:
21
21
  - synchronous SQLAlchemy execution through `Session`
22
22
  - asynchronous SQLAlchemy execution through `AsyncSession`
23
23
 
24
+ For routes that expose one stable model contract, prefer a declarative
25
+ resource. It keeps field policies, default ordering, page limits, and OpenAPI
26
+ examples next to the model they describe.
27
+
28
+ ```python
29
+ users = integration.resource(
30
+ User,
31
+ filterable_fields={"id", "name"},
32
+ sortable_fields={"name"},
33
+ default_sort="name,desc",
34
+ max_page_size=50,
35
+ )
36
+ ```
37
+
38
+ Use `FastAPISQLAlchemyIntegration` directly for dynamic models or when a
39
+ service layer needs to build statements outside a route dependency.
40
+
24
41
  ## Route-ready dependencies
25
42
 
26
43
  ```python
@@ -140,9 +157,8 @@ This avoids leaking raw SQLAlchemy exceptions as `500` responses.
140
157
 
141
158
  ## Free-threaded behavior
142
159
 
143
- The integration caches dependency callables and base `select(model)` statements
144
- per model. Those shared caches are published with explicit synchronization for
145
- free-threaded execution.
160
+ The integration caches base `select(model)` statements per model. That shared
161
+ cache is published with explicit synchronization for free-threaded execution.
146
162
 
147
163
  ## Async note
148
164
 
@@ -40,6 +40,29 @@ config = FastAPICriteriaConfig(
40
40
  dependency = criteria_dependency(config)
41
41
  ```
42
42
 
43
+ ## Repeated sort parameters
44
+
45
+ The default sort representation uses one semicolon-delimited parameter:
46
+
47
+ ```text
48
+ ?sort=name,asc;created_at,desc
49
+ ```
50
+
51
+ To accept one sort field per repeated query parameter, configure the adapter
52
+ with `SortParameterFormat.REPEATED`:
53
+
54
+ ```python
55
+ from pyrsql.adapters.fastapi import SortParameterFormat
56
+
57
+ config = FastAPICriteriaConfig(
58
+ sort_parameter_format=SortParameterFormat.REPEATED,
59
+ )
60
+ ```
61
+
62
+ ```text
63
+ ?sort=name,asc&sort=created_at,desc
64
+ ```
65
+
43
66
  ## Class-based dependency
44
67
 
45
68
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyrsql"
3
- version = "0.0.3"
3
+ version = "0.0.4"
4
4
  description = "Compiler-oriented RSQL query engine for safe, typed, and extensible Python ORM-backed APIs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -4,7 +4,6 @@ from pyrsql import orms
4
4
  from pyrsql.api import apply, compile, parse # noqa: A004
5
5
  from pyrsql.core import (
6
6
  DEFAULT_JSON_OPTIONS,
7
- CompilationResult,
8
7
  CustomPredicateDefinition,
9
8
  FieldPolicySet,
10
9
  FieldValueConverterSet,
@@ -14,13 +13,11 @@ from pyrsql.core import (
14
13
  JSONScalarNormalizer,
15
14
  JSONScalarValue,
16
15
  JSONSortScalarType,
17
- PageCompilationResult,
18
16
  PageRequest,
19
17
  ProcedureAccessPolicy,
20
18
  Query,
21
19
  QueryOptions,
22
20
  Sort,
23
- SortCompilationResult,
24
21
  SortOptions,
25
22
  ValueConverter,
26
23
  ValueConverterRegistry,
@@ -30,7 +27,6 @@ from pyrsql.orms.base import ORM
30
27
  __all__ = (
31
28
  "DEFAULT_JSON_OPTIONS",
32
29
  "ORM",
33
- "CompilationResult",
34
30
  "CustomPredicateDefinition",
35
31
  "FieldPolicySet",
36
32
  "FieldValueConverterSet",
@@ -40,13 +36,11 @@ __all__ = (
40
36
  "JSONScalarNormalizer",
41
37
  "JSONScalarValue",
42
38
  "JSONSortScalarType",
43
- "PageCompilationResult",
44
39
  "PageRequest",
45
40
  "ProcedureAccessPolicy",
46
41
  "Query",
47
42
  "QueryOptions",
48
43
  "Sort",
49
- "SortCompilationResult",
50
44
  "SortOptions",
51
45
  "ValueConverter",
52
46
  "ValueConverterRegistry",
@@ -1,6 +1,9 @@
1
1
  """FastAPI adapter for pyrsql request extraction and error translation."""
2
2
 
3
- from pyrsql.adapters.fastapi.config import FastAPICriteriaConfig
3
+ from pyrsql.adapters.fastapi.config import (
4
+ FastAPICriteriaConfig,
5
+ SortParameterFormat,
6
+ )
4
7
  from pyrsql.adapters.fastapi.criteria import RequestCriteria
5
8
  from pyrsql.adapters.fastapi.dependency import (
6
9
  CriteriaDependency,
@@ -11,5 +14,6 @@ __all__ = (
11
14
  "CriteriaDependency",
12
15
  "FastAPICriteriaConfig",
13
16
  "RequestCriteria",
17
+ "SortParameterFormat",
14
18
  "criteria_dependency",
15
19
  )
@@ -0,0 +1,84 @@
1
+ """Configuration objects for the FastAPI adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import Enum
6
+ from types import MappingProxyType
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ import msgspec
10
+
11
+ from pyrsql.core.options import QueryOptions, SortOptions
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Mapping
15
+
16
+ _DEFAULT_FILTER_PARAMETER = "filter"
17
+ _DEFAULT_SORT_PARAMETER = "sort"
18
+ _DEFAULT_PAGE_PARAMETER = "page"
19
+ _DEFAULT_SIZE_PARAMETER = "size"
20
+ _DEFAULT_QUERY_OPTIONS = QueryOptions()
21
+ _DEFAULT_SORT_OPTIONS = SortOptions()
22
+ _EMPTY_OPENAPI_EXAMPLES: Mapping[str, Any] = MappingProxyType({})
23
+
24
+
25
+ class SortParameterFormat(Enum):
26
+ """Supported HTTP representations for sort criteria."""
27
+
28
+ SEMICOLON = "semicolon"
29
+ REPEATED = "repeated"
30
+
31
+
32
+ class FastAPICriteriaConfig(
33
+ msgspec.Struct,
34
+ frozen=True,
35
+ gc=False,
36
+ kw_only=True,
37
+ ):
38
+ """Configures FastAPI query parameter extraction for pyrsql.
39
+
40
+ Attributes:
41
+ filter_parameter: Query parameter used for filtering expressions.
42
+ sort_parameter: Query parameter used for sort expressions.
43
+ sort_parameter_format: HTTP representation accepted for sort criteria.
44
+ page_parameter: Query parameter used for page numbers.
45
+ size_parameter: Query parameter used for page size.
46
+ default_page_size: Default size used when only a page number is given.
47
+ max_page_size: Maximum accepted page size.
48
+ one_based_paging: Whether page numbers start at 1 instead of 0.
49
+ query_options: Query parsing and semantic configuration.
50
+ sort_options: Sort parsing and semantic configuration.
51
+ """
52
+
53
+ filter_parameter: str = _DEFAULT_FILTER_PARAMETER
54
+ sort_parameter: str = _DEFAULT_SORT_PARAMETER
55
+ sort_parameter_format: SortParameterFormat = SortParameterFormat.SEMICOLON
56
+ page_parameter: str = _DEFAULT_PAGE_PARAMETER
57
+ size_parameter: str = _DEFAULT_SIZE_PARAMETER
58
+ default_page_size: int | None = None
59
+ max_page_size: int | None = None
60
+ one_based_paging: bool = False
61
+ query_options: QueryOptions = _DEFAULT_QUERY_OPTIONS
62
+ sort_options: SortOptions = _DEFAULT_SORT_OPTIONS
63
+ filter_openapi_examples: Mapping[str, Any] = _EMPTY_OPENAPI_EXAMPLES
64
+ sort_openapi_examples: Mapping[str, Any] = _EMPTY_OPENAPI_EXAMPLES
65
+ page_openapi_examples: Mapping[str, Any] = _EMPTY_OPENAPI_EXAMPLES
66
+ size_openapi_examples: Mapping[str, Any] = _EMPTY_OPENAPI_EXAMPLES
67
+
68
+ @property
69
+ def minimum_page_number(self) -> int:
70
+ """The minimum accepted page number for the adapter.
71
+
72
+ Returns:
73
+ The lowest valid page number accepted by the adapter.
74
+ """
75
+ return 1 if self.one_based_paging else 0
76
+
77
+ @property
78
+ def default_page_number(self) -> int:
79
+ """The page number used when only size is provided.
80
+
81
+ Returns:
82
+ The implicit page number used when only page size is provided.
83
+ """
84
+ return 1 if self.one_based_paging else 0
@@ -53,8 +53,6 @@ class RequestCriteria(
53
53
  Returns:
54
54
  The transformed ORM-specific target after applying all criteria.
55
55
  """
56
- if self.is_empty:
57
- return target
58
56
  current_target = target
59
57
  if self.query is not None:
60
58
  current_target = self.query.apply(