pyrsql 0.0.3__tar.gz → 0.0.5__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 (201) hide show
  1. {pyrsql-0.0.3 → pyrsql-0.0.5}/PKG-INFO +64 -20
  2. {pyrsql-0.0.3 → pyrsql-0.0.5}/README.md +58 -13
  3. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/explanation/architecture.md +4 -5
  4. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/explanation/extensibility.md +9 -17
  5. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/index.md +28 -10
  6. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/quickstart.md +40 -2
  7. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/reference/api.md +1 -2
  8. pyrsql-0.0.5/docs/reference/operators.md +51 -0
  9. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/reference/options.md +4 -0
  10. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/testing.md +0 -1
  11. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/fastapi-sqlalchemy.md +20 -4
  12. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/fastapi.md +23 -0
  13. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/query.md +9 -5
  14. pyrsql-0.0.5/pyproject.toml +233 -0
  15. pyrsql-0.0.3/pyproject.toml → pyrsql-0.0.5/pyproject.toml.orig +21 -19
  16. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/__init__.py +0 -6
  17. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/adapters/fastapi/__init__.py +5 -1
  18. pyrsql-0.0.5/src/pyrsql/adapters/fastapi/config.py +84 -0
  19. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/adapters/fastapi/criteria.py +0 -2
  20. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/adapters/fastapi/dependency.py +130 -53
  21. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/adapters/fastapi/errors.py +9 -14
  22. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/api.py +3 -3
  23. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/__init__.py +0 -8
  24. pyrsql-0.0.5/src/pyrsql/core/compiler.py +15 -0
  25. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/conversion.py +2 -3
  26. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/json/options.py +2 -2
  27. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/options.py +41 -153
  28. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/page.py +4 -9
  29. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/query.py +4 -8
  30. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/sort.py +4 -8
  31. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/source_issues.py +1 -1
  32. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/helpers.py +4 -16
  33. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/integration.py +21 -103
  34. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/resource.py +10 -80
  35. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/__init__.py +0 -6
  36. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/base.py +5 -19
  37. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/compiled_page.py +3 -4
  38. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/compiled_query.py +1 -1
  39. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/compiled_sort.py +1 -1
  40. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/introspection.py +4 -16
  41. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/json_path.py +6 -9
  42. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/orm.py +11 -34
  43. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/resolver.py +1 -17
  44. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/sorter.py +7 -15
  45. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/translator.py +5 -18
  46. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/type_inference.py +14 -16
  47. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/types.py +0 -2
  48. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/__init__.py +1 -4
  49. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/ast.py +4 -20
  50. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/errors.py +0 -2
  51. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/lexer.py +13 -35
  52. pyrsql-0.0.5/src/pyrsql/parsing/limits.py +19 -0
  53. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/operators.py +1 -92
  54. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/parser.py +8 -15
  55. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/source.py +0 -23
  56. pyrsql-0.0.5/src/pyrsql/selector/ast.py +31 -0
  57. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/selector/parser.py +34 -72
  58. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/semantic/binder.py +0 -12
  59. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/binder.py +0 -6
  60. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/errors.py +1 -1
  61. pyrsql-0.0.5/src/pyrsql/sorting/limits.py +16 -0
  62. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/parser.py +3 -36
  63. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_adapter/test_dependency.py +27 -0
  64. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/test_public_api.py +3 -7
  65. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/test_async_pipeline.py +16 -2
  66. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/test_query_pipeline.py +58 -16
  67. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_hpp.py +21 -1
  68. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/adapters/fastapi_adapter/conftest.py +2 -0
  69. pyrsql-0.0.5/tests/unit/adapters/fastapi_adapter/test_config.py +39 -0
  70. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/adapters/fastapi_adapter/test_criteria.py +4 -13
  71. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/conftest.py +0 -5
  72. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_conversion.py +1 -1
  73. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_options.py +2 -0
  74. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_page.py +3 -3
  75. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_query.py +3 -3
  76. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_sort.py +3 -3
  77. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/fastapi_sqlalchemy/conftest.py +4 -0
  78. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +0 -182
  79. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/test_json_path_builder.py +1 -1
  80. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/test_resolver.py +1 -1
  81. pyrsql-0.0.5/tests/unit/orms/sqlalchemy/test_type_inference.py +43 -0
  82. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/parsing/test_lexer.py +37 -32
  83. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/parsing/test_parser.py +0 -72
  84. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/parsing/test_source.py +1 -6
  85. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/selector/test_parser.py +10 -88
  86. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/sorting/test_parser.py +0 -62
  87. pyrsql-0.0.3/docs/reference/operators.md +0 -37
  88. pyrsql-0.0.3/src/pyrsql/adapters/fastapi/config.py +0 -169
  89. pyrsql-0.0.3/src/pyrsql/core/compiler.py +0 -45
  90. pyrsql-0.0.3/src/pyrsql/parsing/diagnostics.py +0 -9
  91. pyrsql-0.0.3/src/pyrsql/parsing/limits.py +0 -48
  92. pyrsql-0.0.3/src/pyrsql/parsing/normalization.py +0 -51
  93. pyrsql-0.0.3/src/pyrsql/selector/ast.py +0 -45
  94. pyrsql-0.0.3/src/pyrsql/sorting/limits.py +0 -33
  95. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/test_config.py +0 -111
  96. pyrsql-0.0.3/tests/unit/parsing/test_operators.py +0 -105
  97. pyrsql-0.0.3/tests/unit/selector/test_ast.py +0 -30
  98. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/contributing.md +0 -0
  99. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/async.md +0 -0
  100. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/custom-predicates.md +0 -0
  101. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/errors.md +0 -0
  102. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/field-policies.md +0 -0
  103. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/join-hints.md +0 -0
  104. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/json.md +0 -0
  105. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/page.md +0 -0
  106. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/sort.md +0 -0
  107. {pyrsql-0.0.3 → pyrsql-0.0.5}/docs/usage/value-conversion.md +0 -0
  108. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/adapters/__init__.py +0 -0
  109. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/binding_policy.py +0 -0
  110. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/custom.py +0 -0
  111. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/field_policy.py +0 -0
  112. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/joins.py +0 -0
  113. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/json/__init__.py +0 -0
  114. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/json/path.py +0 -0
  115. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/json/query.py +0 -0
  116. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/json/values.py +0 -0
  117. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/procedure_policy.py +0 -0
  118. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/core/validation.py +0 -0
  119. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/__init__.py +0 -0
  120. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/__init__.py +0 -0
  121. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/__init__.py +0 -0
  122. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/examples.py +0 -0
  123. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/integrations/fastapi/sqlalchemy/payloads.py +0 -0
  124. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/__init__.py +0 -0
  125. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/coercion.py +0 -0
  126. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/custom.py +0 -0
  127. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/errors.py +0 -0
  128. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/orms/sqlalchemy/statement.py +0 -0
  129. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/parsing/tokens.py +0 -0
  130. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/py.typed +0 -0
  131. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/selector/__init__.py +0 -0
  132. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/semantic/__init__.py +0 -0
  133. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/semantic/diagnostics.py +0 -0
  134. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/semantic/errors.py +0 -0
  135. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/__init__.py +0 -0
  136. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/ast.py +0 -0
  137. {pyrsql-0.0.3 → pyrsql-0.0.5}/src/pyrsql/sorting/diagnostics.py +0 -0
  138. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/__init__.py +0 -0
  139. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/conftest.py +0 -0
  140. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/__init__.py +0 -0
  141. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_adapter/__init__.py +0 -0
  142. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_adapter/test_async_dependency.py +0 -0
  143. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_sqlalchemy/__init__.py +0 -0
  144. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_sqlalchemy/conftest.py +0 -0
  145. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_sqlalchemy/test_async_integration.py +0 -0
  146. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/functional/fastapi_sqlalchemy/test_integration.py +0 -0
  147. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/__init__.py +0 -0
  148. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/__init__.py +0 -0
  149. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/conftest.py +0 -0
  150. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/test_page_pipeline.py +0 -0
  151. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/integration/sqlalchemy/test_sort_pipeline.py +0 -0
  152. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/__init__.py +0 -0
  153. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/conftest.py +0 -0
  154. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_conversion_bench.py +0 -0
  155. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_json_bench.py +0 -0
  156. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_json_path_builder_bench.py +0 -0
  157. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_parser_bench.py +0 -0
  158. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_path_resolver_bench.py +0 -0
  159. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_selector_bench.py +0 -0
  160. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_semantic_bench.py +0 -0
  161. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/performance/test_sqlalchemy_translation_bench.py +0 -0
  162. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/__init__.py +0 -0
  163. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/conftest.py +0 -0
  164. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_configuration_hardening.py +0 -0
  165. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_error_sanitization.py +0 -0
  166. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_injection.py +0 -0
  167. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_limits.py +0 -0
  168. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_policies.py +0 -0
  169. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_fastapi_sqlalchemy_resource_security.py +0 -0
  170. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_sqlalchemy_injection.py +0 -0
  171. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/security/test_sqlalchemy_jsonpath_security.py +0 -0
  172. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/__init__.py +0 -0
  173. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/adapters/__init__.py +0 -0
  174. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/adapters/fastapi_adapter/__init__.py +0 -0
  175. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/adapters/fastapi_adapter/test_errors.py +0 -0
  176. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/conftest.py +0 -0
  177. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/__init__.py +0 -0
  178. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/json/__init__.py +0 -0
  179. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/json/test_options.py +0 -0
  180. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/json/test_path.py +0 -0
  181. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/json/test_query.py +0 -0
  182. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/json/test_values.py +0 -0
  183. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/core/test_custom.py +0 -0
  184. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/__init__.py +0 -0
  185. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/fastapi/__init__.py +0 -0
  186. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/fastapi_sqlalchemy/__init__.py +0 -0
  187. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/integrations/fastapi_sqlalchemy/test_examples.py +0 -0
  188. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/__init__.py +0 -0
  189. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/__init__.py +0 -0
  190. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/conftest.py +0 -0
  191. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/test_coercion.py +0 -0
  192. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/test_orm.py +0 -0
  193. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/orms/sqlalchemy/test_statement.py +0 -0
  194. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/parsing/__init__.py +0 -0
  195. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/parsing/test_ast.py +0 -0
  196. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/selector/__init__.py +0 -0
  197. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/semantic/__init__.py +0 -0
  198. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/semantic/test_binder.py +0 -0
  199. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/sorting/__init__.py +0 -0
  200. {pyrsql-0.0.3 → pyrsql-0.0.5}/tests/unit/sorting/test_ast.py +0 -0
  201. {pyrsql-0.0.3 → pyrsql-0.0.5}/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.5
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
@@ -9,7 +9,6 @@ Classifier: Environment :: Web Environment
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: Intended Audience :: Information Technology
11
11
  Classifier: Operating System :: OS Independent
12
- Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
@@ -20,11 +19,11 @@ Classifier: Topic :: Database
20
19
  Classifier: Topic :: Software Development :: Compilers
21
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
21
  Classifier: Typing :: Typed
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
22
+ Requires-Dist: msgspec>=0.22.0,<1.0
23
+ Requires-Dist: fastapi>=0.150.0,<1.0 ; extra == 'fastapi'
24
+ Requires-Dist: sqlalchemy[asyncio]>=2.1,<3.0 ; extra == 'sqlalchemy'
25
+ Requires-Dist: typing-extensions>=4.10.0 ; extra == 'sqlalchemy'
26
+ Requires-Python: >=3.11
28
27
  Project-URL: Homepage, https://github.com/wskr00/pyrsql
29
28
  Project-URL: Repository, https://github.com/wskr00/pyrsql
30
29
  Project-URL: Issues, https://github.com/wskr00/pyrsql/issues
@@ -38,7 +37,7 @@ Description-Content-Type: text/markdown
38
37
  A compiler-oriented RSQL query engine for safe, typed, and extensible
39
38
  filtering, sorting, and pagination in Python APIs.
40
39
 
41
- [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14%20%7C%203.14t-blue?logo=python)](https://pypi.org/project/pyrsql/)
40
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14%20%7C%203.14t-blue?logo=python)](https://pypi.org/project/pyrsql/)
42
41
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
43
42
 
44
43
  pyrsql compiles RSQL query strings into ORM-specific statement objects
@@ -46,9 +45,14 @@ through a language frontend, semantic binding, and pluggable backend
46
45
  lowering - making it easy to expose complex query capabilities in your API
47
46
  without coupling to a specific ORM or framework.
48
47
 
49
- **Current backends:** SQLAlchemy 2.0
50
- **Current framework adapters:** FastAPI
51
- **Planned:** Django ORM, SQLModel, Flask
48
+ For FastAPI applications using SQLAlchemy, the recommended entry point is
49
+ `FastAPISQLAlchemyIntegration`. It provides route-ready dependencies and
50
+ declarative resources; the lower-level query, adapter, and ORM APIs remain
51
+ available when more control is needed.
52
+
53
+ - **Current backends:** SQLAlchemy 2.1+
54
+ - **Current framework adapters:** FastAPI
55
+ - **Planned:** Django ORM, SQLModel, Flask
52
56
 
53
57
  ## Why pyrsql?
54
58
 
@@ -73,9 +77,41 @@ language.
73
77
  ## Quickstart
74
78
 
75
79
  ```bash
76
- pip install pyrsql[sqlalchemy]
80
+ pip install pyrsql[fastapi,sqlalchemy]
77
81
  ```
78
82
 
83
+ ```python
84
+ from typing import Annotated, Any
85
+
86
+ from fastapi import Depends, FastAPI
87
+
88
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
89
+
90
+ app = FastAPI()
91
+ integration = FastAPISQLAlchemyIntegration()
92
+ users = integration.resource(
93
+ User,
94
+ filterable_fields={"id", "name"},
95
+ sortable_fields={"name"},
96
+ default_sort="name,asc",
97
+ max_page_size=100,
98
+ )
99
+
100
+ @app.get("/users")
101
+ def list_users(
102
+ stmt: Annotated[Any, Depends(users.select_dependency())],
103
+ ):
104
+ return {"sql": str(stmt)}
105
+ ```
106
+
107
+ Clients can now use `filter`, `sort`, `page`, and `size` query parameters.
108
+ See [FastAPI + SQLAlchemy](https://wskr00.github.io/pyrsql/usage/fastapi-sqlalchemy/)
109
+ for count, pagination, custom base statements, error behavior, and async use.
110
+
111
+ ### Lower-level SQLAlchemy API
112
+
113
+ Use the direct API when you do not need a FastAPI route dependency:
114
+
79
115
  ```python
80
116
  import pyrsql
81
117
  from sqlalchemy import select
@@ -84,13 +120,13 @@ from pyrsql.orms.sqlalchemy import SQLAlchemyORM
84
120
  orm = SQLAlchemyORM()
85
121
  stmt = select(User)
86
122
 
87
- # Filter: name equals "demo", company name contains "acme"
88
- stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
123
+ stmt = pyrsql.parse("name==demo;company.name==acme*").apply(
124
+ stmt,
125
+ User,
126
+ orm=orm,
127
+ )
89
128
 
90
- # Sort: by name ascending, then company name descending
91
129
  stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
92
-
93
- # Paginate: page 0, 25 items per page
94
130
  stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
95
131
  ```
96
132
 
@@ -168,6 +204,9 @@ options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
168
204
 
169
205
  ### FastAPI Integration
170
206
 
207
+ Use this adapter directly when you need only parsed `RequestCriteria`; for
208
+ SQLAlchemy routes, prefer the integration below.
209
+
171
210
  ```python
172
211
  from fastapi import Depends, FastAPI
173
212
  from pyrsql.adapters.fastapi import criteria_dependency
@@ -181,10 +220,11 @@ def list_items(criteria = Depends(dependency)):
181
220
  ```
182
221
 
183
222
  - Auto-extracts `filter`, `sort`, `page`, `size` from query params
184
- - `HTTP 422` with structured diagnostics on parse/semantic errors
223
+ - Structured `HTTP 400` parse errors and `HTTP 422` semantic errors
185
224
  - OpenAPI examples from configuration
186
225
  - One-based paging support
187
226
  - Custom query parameter names
227
+ - Optional repeated sort parameters through `SortParameterFormat.REPEATED`
188
228
 
189
229
  ### FastAPI + SQLAlchemy Integration
190
230
 
@@ -199,7 +239,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
199
239
  ```
200
240
 
201
241
  - `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
202
- - Declarative `resource()` with auto-generated OpenAPI examples
242
+ - Declarative `resource()` with auto-generated OpenAPI examples and field policies
203
243
  - `applier_dependency` for custom base statements
204
244
  - Compatible with both sync `Session` and async `AsyncSession` execution
205
245
  - FastAPI parse and page-validation failures become structured `HTTP 400` payloads
@@ -207,7 +247,7 @@ def list_users(stmt = Depends(integration.select_dependency(User))):
207
247
 
208
248
  ### Concurrency and Validation
209
249
 
210
- - Shared integration and ORM metadata caches are protected for free-threaded execution
250
+ - Shared base-select and ORM metadata caches are protected for free-threaded execution
211
251
  - Async support is validated for adapter, ORM, and integration flows
212
252
  - Dedicated async, free-threaded, and security test suites validate these flows
213
253
 
@@ -235,6 +275,10 @@ Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsq
235
275
  - Strong typing
236
276
  - ORM-neutral public API
237
277
 
278
+ ## Acknowledgements
279
+
280
+ `pyrsql` was strongly inspired by [Jirutka's `rsql-parser`](https://github.com/jirutka/rsql-parser) and [PerplexHub's `rsql-jpa-specification`](https://github.com/perplexhub/rsql-jpa-specification), especially their RSQL syntax and filtering behavior. I am grateful to their maintainers and contributors for their work.
281
+
238
282
  ## License
239
283
 
240
284
  [MIT](LICENSE).
@@ -3,7 +3,7 @@
3
3
  A compiler-oriented RSQL query engine for safe, typed, and extensible
4
4
  filtering, sorting, and pagination in Python APIs.
5
5
 
6
- [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14%20%7C%203.14t-blue?logo=python)](https://pypi.org/project/pyrsql/)
6
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14%20%7C%203.14t-blue?logo=python)](https://pypi.org/project/pyrsql/)
7
7
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
8
 
9
9
  pyrsql compiles RSQL query strings into ORM-specific statement objects
@@ -11,9 +11,14 @@ 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
- **Current backends:** SQLAlchemy 2.0
15
- **Current framework adapters:** FastAPI
16
- **Planned:** Django ORM, SQLModel, Flask
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
+
19
+ - **Current backends:** SQLAlchemy 2.1+
20
+ - **Current framework adapters:** FastAPI
21
+ - **Planned:** Django ORM, SQLModel, Flask
17
22
 
18
23
  ## Why pyrsql?
19
24
 
@@ -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
 
@@ -200,6 +241,10 @@ Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsq
200
241
  - Strong typing
201
242
  - ORM-neutral public API
202
243
 
244
+ ## Acknowledgements
245
+
246
+ `pyrsql` was strongly inspired by [Jirutka's `rsql-parser`](https://github.com/jirutka/rsql-parser) and [PerplexHub's `rsql-jpa-specification`](https://github.com/perplexhub/rsql-jpa-specification), especially their RSQL syntax and filtering behavior. I am grateful to their maintainers and contributors for their work.
247
+
203
248
  ## License
204
249
 
205
250
  [MIT](LICENSE).
@@ -28,7 +28,7 @@ The project is designed around four major responsibilities:
28
28
  - backend lowering: turn validated query objects into ORM-specific statements
29
29
  - optional framework adapters and integrations: expose the core in HTTP stacks
30
30
 
31
- The current production backend is SQLAlchemy 2.0. The current framework
31
+ The current production backend is SQLAlchemy 2.1+. The current framework
32
32
  adapter is FastAPI.
33
33
 
34
34
  ## Current Pipeline
@@ -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,24 +10,42 @@ 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
30
- - **Pluggable backends** - SQLAlchemy 2.0 today, Django ORM / SQLModel planned
48
+ - **Pluggable backends** - SQLAlchemy 2.1+ today, Django ORM / SQLModel planned
31
49
  - **Pluggable adapters** - FastAPI today, Flask planned
32
50
  - **20+ built-in operators** - `==`, `=like=`, `=in=`, `=bt=`, `=nn=`, etc.
33
51
  - **Custom operators** - define your own RSQL operators with per-ORM lowering
@@ -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
@@ -17,7 +55,7 @@ from pyrsql import Query
17
55
  Query.parse("name==demo") # equal
18
56
  Query.parse("age=gt=18") # greater than
19
57
  Query.parse("id=in=(1,2,3)") # IN
20
- Query.parse("name=like=demo*") # LIKE
58
+ Query.parse("name=like=demo") # contains: LIKE '%demo%'
21
59
  Query.parse("score=bt=(10,20)") # BETWEEN
22
60
  Query.parse("name==demo;age=gt=18") # AND
23
61
  Query.parse("name==demo,age=gt=18") # OR
@@ -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
@@ -0,0 +1,51 @@
1
+ # Operators
2
+
3
+ Pyrsql supports the base RSQL comparison operators and the extensions used by
4
+ PerplexHub's JPA specification.
5
+
6
+ ## Comparison operators
7
+
8
+ | Operator | Aliases | Arity | Description |
9
+ |----------|---------|-------|-------------|
10
+ | `==` | | 1 | Equal |
11
+ | `!=` | | 1 | Not equal |
12
+ | `=gt=` | `>` | 1 | Greater than |
13
+ | `=ge=` | `>=` | 1 | Greater or equal |
14
+ | `=lt=` | `<` | 1 | Less than |
15
+ | `=le=` | `<=` | 1 | Less or equal |
16
+ | `=in=` | | 1..100 | IN (comma-separated args) |
17
+ | `=out=` | | 1..100 | NOT IN |
18
+ | `=like=` | `=ke=` | 1 | Contains match (`LIKE '%value%'`) |
19
+ | `=notlike=` | `=nk=` | 1 | Negated contains match |
20
+ | `=ilike=` | `=ik=` | 1 | Case-insensitive contains match |
21
+ | `=inotlike=` | `=ni=` | 1 | Negated case-insensitive contains match |
22
+ | `=ic=` | `=icase=` | 1 | Case-insensitive equality |
23
+ | `=bt=` | `=between=` | 2 | BETWEEN (exactly 2 args) |
24
+ | `=nb=` | `=notbetween=` | 2 | NOT BETWEEN (exactly 2 args) |
25
+ | `=na=` | `=isnull=`, `=null=` | 0..1 | IS NULL |
26
+ | `=nn=` | `=notnull=`, `=isnotnull=` | 0..1 | IS NOT NULL |
27
+
28
+ For `=like=`, `=notlike=`, `=ilike=`, and `=inotlike=`, pyrsql surrounds the
29
+ argument with `%` to match anywhere in the value. These operators use SQL
30
+ `LIKE` pattern rules: `%` matches any number of characters, `_` matches one,
31
+ and `*` is literal. The `like_escape_character` option controls SQL pattern
32
+ escaping.
33
+
34
+ For string-column `==` and `!=`, `*` is the wildcard and any `^` marker
35
+ requests case-insensitive matching. Quoting an argument does not disable these
36
+ markers. Use `=in=(*)` to match a literal asterisk.
37
+
38
+ ## Logical operators
39
+
40
+ | Operator | Aliases | Description |
41
+ |----------|---------|-------------|
42
+ | `;` | `and` | AND (higher precedence) |
43
+ | `,` | `or` | OR (lower precedence) |
44
+
45
+ ## Sort modifiers
46
+
47
+ | Modifier | Example | Description |
48
+ |----------|---------|-------------|
49
+ | `asc` | `name,asc` | Ascending (default) |
50
+ | `desc` | `name,desc` | Descending |
51
+ | `ic` | `name,desc,ic` | Ignore case |
@@ -26,6 +26,10 @@
26
26
 
27
27
  Notes:
28
28
 
29
+ - `strict_equality=True` makes string `==` and `!=` compare markers such as
30
+ `*` and `^` literally; quoting alone does not disable them
31
+ - `like_escape_character` applies to LIKE-based operators, including equality
32
+ comparisons that contain `*`
29
33
  - configuration containers are normalized into immutable views
30
34
  - procedure allow/block rules are compiled as regex policies and evaluated with
31
35
  `fullmatch`, not partial match
@@ -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