pyrsql 0.0.1__tar.gz → 0.0.3__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 (268) hide show
  1. pyrsql-0.0.3/PKG-INFO +240 -0
  2. pyrsql-0.0.3/README.md +205 -0
  3. pyrsql-0.0.3/docs/contributing.md +49 -0
  4. pyrsql-0.0.3/docs/explanation/architecture.md +245 -0
  5. pyrsql-0.0.3/docs/explanation/extensibility.md +90 -0
  6. pyrsql-0.0.3/docs/index.md +58 -0
  7. pyrsql-0.0.3/docs/quickstart.md +143 -0
  8. pyrsql-0.0.3/docs/reference/api.md +42 -0
  9. pyrsql-0.0.3/docs/reference/operators.md +37 -0
  10. pyrsql-0.0.3/docs/reference/options.md +107 -0
  11. pyrsql-0.0.3/docs/testing.md +185 -0
  12. pyrsql-0.0.3/docs/usage/async.md +138 -0
  13. pyrsql-0.0.3/docs/usage/custom-predicates.md +53 -0
  14. pyrsql-0.0.3/docs/usage/errors.md +213 -0
  15. pyrsql-0.0.3/docs/usage/fastapi-sqlalchemy.md +150 -0
  16. pyrsql-0.0.3/docs/usage/fastapi.md +108 -0
  17. pyrsql-0.0.3/docs/usage/field-policies.md +46 -0
  18. pyrsql-0.0.3/docs/usage/join-hints.md +20 -0
  19. pyrsql-0.0.3/docs/usage/json.md +87 -0
  20. pyrsql-0.0.3/docs/usage/page.md +31 -0
  21. pyrsql-0.0.3/docs/usage/query.md +125 -0
  22. pyrsql-0.0.3/docs/usage/sort.md +42 -0
  23. pyrsql-0.0.3/docs/usage/value-conversion.md +44 -0
  24. pyrsql-0.0.3/pyproject.toml +220 -0
  25. pyrsql-0.0.3/src/pyrsql/__init__.py +57 -0
  26. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/adapters/__init__.py +1 -1
  27. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/adapters/fastapi/__init__.py +2 -2
  28. pyrsql-0.0.3/src/pyrsql/adapters/fastapi/config.py +169 -0
  29. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/adapters/fastapi/criteria.py +34 -13
  30. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/adapters/fastapi/dependency.py +73 -33
  31. pyrsql-0.0.3/src/pyrsql/adapters/fastapi/errors.py +347 -0
  32. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/api.py +15 -14
  33. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/__init__.py +7 -1
  34. pyrsql-0.0.3/src/pyrsql/core/binding_policy.py +78 -0
  35. pyrsql-0.0.3/src/pyrsql/core/compiler.py +45 -0
  36. pyrsql-0.0.3/src/pyrsql/core/conversion.py +405 -0
  37. pyrsql-0.0.3/src/pyrsql/core/custom.py +30 -0
  38. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/field_policy.py +84 -24
  39. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/json/__init__.py +8 -7
  40. pyrsql-0.0.3/src/pyrsql/core/json/options.py +144 -0
  41. pyrsql-0.0.3/src/pyrsql/core/json/path.py +92 -0
  42. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/json/query.py +29 -9
  43. pyrsql-0.0.3/src/pyrsql/core/json/values.py +114 -0
  44. pyrsql-0.0.3/src/pyrsql/core/options.py +586 -0
  45. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/page.py +58 -23
  46. pyrsql-0.0.3/src/pyrsql/core/procedure_policy.py +173 -0
  47. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/query.py +40 -57
  48. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/sort.py +37 -54
  49. pyrsql-0.0.3/src/pyrsql/core/source_issues.py +64 -0
  50. pyrsql-0.0.3/src/pyrsql/core/validation.py +16 -0
  51. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/integrations/__init__.py +6 -1
  52. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/integrations/fastapi/__init__.py +6 -1
  53. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/__init__.py +17 -0
  54. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/examples.py +215 -0
  55. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/helpers.py +162 -0
  56. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/integration.py +457 -0
  57. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/payloads.py +22 -0
  58. pyrsql-0.0.3/src/pyrsql/integrations/fastapi/sqlalchemy/resource.py +359 -0
  59. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/__init__.py +2 -0
  60. pyrsql-0.0.3/src/pyrsql/orms/base.py +72 -0
  61. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/__init__.py +5 -5
  62. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/coercion.py +114 -0
  63. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/compiled_page.py +16 -6
  64. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/compiled.py → pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/compiled_query.py +19 -9
  65. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/compiled_sort.py +22 -10
  66. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/custom.py +12 -5
  67. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/errors.py +14 -2
  68. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/introspection.py +107 -27
  69. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/json_path.py +653 -0
  70. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/orm.py +119 -0
  71. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/resolver.py +322 -0
  72. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/sorter.py +238 -0
  73. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/statement.py +85 -0
  74. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/translator.py +296 -205
  75. pyrsql-0.0.3/src/pyrsql/orms/sqlalchemy/type_inference.py +59 -0
  76. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/orms/sqlalchemy/types.py +31 -16
  77. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/parsing/__init__.py +7 -5
  78. pyrsql-0.0.3/src/pyrsql/parsing/ast.py +89 -0
  79. pyrsql-0.0.3/src/pyrsql/parsing/diagnostics.py +9 -0
  80. pyrsql-0.0.3/src/pyrsql/parsing/errors.py +21 -0
  81. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/parsing/lexer.py +151 -66
  82. pyrsql-0.0.3/src/pyrsql/parsing/limits.py +48 -0
  83. pyrsql-0.0.3/src/pyrsql/parsing/normalization.py +51 -0
  84. pyrsql-0.0.3/src/pyrsql/parsing/operators.py +291 -0
  85. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/parsing/parser.py +164 -47
  86. pyrsql-0.0.3/src/pyrsql/parsing/source.py +52 -0
  87. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/parsing/tokens.py +8 -4
  88. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/selector/__init__.py +4 -14
  89. pyrsql-0.0.3/src/pyrsql/selector/ast.py +45 -0
  90. pyrsql-0.0.3/src/pyrsql/selector/parser.py +276 -0
  91. pyrsql-0.0.3/src/pyrsql/semantic/__init__.py +21 -0
  92. pyrsql-0.0.3/src/pyrsql/semantic/binder.py +155 -0
  93. pyrsql-0.0.3/src/pyrsql/semantic/diagnostics.py +9 -0
  94. pyrsql-0.0.3/src/pyrsql/semantic/errors.py +39 -0
  95. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/sorting/__init__.py +6 -2
  96. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/sorting/ast.py +12 -7
  97. pyrsql-0.0.3/src/pyrsql/sorting/binder.py +118 -0
  98. pyrsql-0.0.3/src/pyrsql/sorting/diagnostics.py +20 -0
  99. pyrsql-0.0.3/src/pyrsql/sorting/errors.py +68 -0
  100. pyrsql-0.0.3/src/pyrsql/sorting/limits.py +33 -0
  101. pyrsql-0.0.3/src/pyrsql/sorting/parser.py +251 -0
  102. pyrsql-0.0.3/tests/__init__.py +1 -0
  103. pyrsql-0.0.3/tests/conftest.py +1 -0
  104. pyrsql-0.0.3/tests/functional/fastapi_adapter/test_async_dependency.py +114 -0
  105. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/functional/fastapi_adapter/test_dependency.py +84 -9
  106. pyrsql-0.0.3/tests/functional/fastapi_sqlalchemy/conftest.py +24 -0
  107. pyrsql-0.0.3/tests/functional/fastapi_sqlalchemy/test_async_integration.py +234 -0
  108. pyrsql-0.0.3/tests/functional/fastapi_sqlalchemy/test_integration.py +249 -0
  109. pyrsql-0.0.3/tests/functional/test_public_api.py +175 -0
  110. pyrsql-0.0.3/tests/integration/sqlalchemy/conftest.py +122 -0
  111. pyrsql-0.0.3/tests/integration/sqlalchemy/test_async_pipeline.py +104 -0
  112. pyrsql-0.0.3/tests/integration/sqlalchemy/test_page_pipeline.py +37 -0
  113. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/integration/sqlalchemy/test_query_pipeline.py +166 -198
  114. pyrsql-0.0.3/tests/integration/sqlalchemy/test_sort_pipeline.py +212 -0
  115. pyrsql-0.0.3/tests/performance/conftest.py +67 -0
  116. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/test_conversion_bench.py +7 -7
  117. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/test_json_bench.py +7 -7
  118. pyrsql-0.0.3/tests/performance/test_json_path_builder_bench.py +72 -0
  119. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/test_parser_bench.py +4 -8
  120. pyrsql-0.0.3/tests/performance/test_path_resolver_bench.py +69 -0
  121. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/test_selector_bench.py +3 -3
  122. pyrsql-0.0.3/tests/performance/test_semantic_bench.py +35 -0
  123. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/test_sqlalchemy_translation_bench.py +17 -30
  124. pyrsql-0.0.3/tests/security/__init__.py +1 -0
  125. pyrsql-0.0.3/tests/security/conftest.py +109 -0
  126. pyrsql-0.0.3/tests/security/test_configuration_hardening.py +30 -0
  127. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_error_sanitization.py +101 -0
  128. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_hpp.py +86 -0
  129. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_injection.py +49 -0
  130. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_limits.py +215 -0
  131. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_policies.py +183 -0
  132. pyrsql-0.0.3/tests/security/test_fastapi_sqlalchemy_resource_security.py +54 -0
  133. pyrsql-0.0.3/tests/security/test_sqlalchemy_injection.py +77 -0
  134. pyrsql-0.0.3/tests/security/test_sqlalchemy_jsonpath_security.py +70 -0
  135. pyrsql-0.0.3/tests/unit/__init__.py +1 -0
  136. pyrsql-0.0.3/tests/unit/adapters/__init__.py +1 -0
  137. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/conftest.py +65 -0
  138. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/test_config.py +111 -0
  139. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/test_criteria.py +122 -0
  140. pyrsql-0.0.3/tests/unit/adapters/fastapi_adapter/test_errors.py +30 -0
  141. pyrsql-0.0.3/tests/unit/conftest.py +12 -0
  142. pyrsql-0.0.3/tests/unit/core/conftest.py +97 -0
  143. pyrsql-0.0.3/tests/unit/core/json/test_options.py +112 -0
  144. pyrsql-0.0.3/tests/unit/core/json/test_path.py +31 -0
  145. pyrsql-0.0.3/tests/unit/core/json/test_query.py +73 -0
  146. pyrsql-0.0.3/tests/unit/core/json/test_values.py +72 -0
  147. pyrsql-0.0.3/tests/unit/core/test_conversion.py +251 -0
  148. pyrsql-0.0.3/tests/unit/core/test_custom.py +22 -0
  149. pyrsql-0.0.3/tests/unit/core/test_options.py +322 -0
  150. pyrsql-0.0.3/tests/unit/core/test_page.py +89 -0
  151. pyrsql-0.0.3/tests/unit/core/test_query.py +103 -0
  152. pyrsql-0.0.3/tests/unit/core/test_sort.py +89 -0
  153. pyrsql-0.0.3/tests/unit/integrations/fastapi_sqlalchemy/conftest.py +82 -0
  154. pyrsql-0.0.3/tests/unit/integrations/fastapi_sqlalchemy/test_examples.py +97 -0
  155. pyrsql-0.0.3/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +614 -0
  156. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/conftest.py +83 -0
  157. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/test_coercion.py +81 -0
  158. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/test_json_path_builder.py +97 -0
  159. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/test_orm.py +26 -0
  160. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/test_resolver.py +330 -0
  161. pyrsql-0.0.3/tests/unit/orms/sqlalchemy/test_statement.py +61 -0
  162. pyrsql-0.0.3/tests/unit/parsing/test_ast.py +66 -0
  163. pyrsql-0.0.3/tests/unit/parsing/test_lexer.py +168 -0
  164. pyrsql-0.0.3/tests/unit/parsing/test_operators.py +105 -0
  165. pyrsql-0.0.3/tests/unit/parsing/test_parser.py +247 -0
  166. pyrsql-0.0.3/tests/unit/parsing/test_source.py +27 -0
  167. pyrsql-0.0.3/tests/unit/selector/test_ast.py +30 -0
  168. pyrsql-0.0.3/tests/unit/selector/test_parser.py +223 -0
  169. pyrsql-0.0.3/tests/unit/semantic/test_binder.py +213 -0
  170. pyrsql-0.0.3/tests/unit/sorting/test_ast.py +26 -0
  171. pyrsql-0.0.3/tests/unit/sorting/test_binder.py +180 -0
  172. pyrsql-0.0.3/tests/unit/sorting/test_parser.py +210 -0
  173. pyrsql-0.0.1/PKG-INFO +0 -72
  174. pyrsql-0.0.1/README.md +0 -43
  175. pyrsql-0.0.1/docs/architecture.md +0 -461
  176. pyrsql-0.0.1/docs/pyguide.md +0 -3709
  177. pyrsql-0.0.1/docs/quality.md +0 -94
  178. pyrsql-0.0.1/docs/reference.md +0 -199
  179. pyrsql-0.0.1/docs/testing.md +0 -228
  180. pyrsql-0.0.1/docs/usage.md +0 -631
  181. pyrsql-0.0.1/pyproject.toml +0 -117
  182. pyrsql-0.0.1/src/pyrsql/__init__.py +0 -63
  183. pyrsql-0.0.1/src/pyrsql/adapters/fastapi/config.py +0 -76
  184. pyrsql-0.0.1/src/pyrsql/adapters/fastapi/errors.py +0 -107
  185. pyrsql-0.0.1/src/pyrsql/core/compiler.py +0 -81
  186. pyrsql-0.0.1/src/pyrsql/core/conversion.py +0 -206
  187. pyrsql-0.0.1/src/pyrsql/core/custom.py +0 -14
  188. pyrsql-0.0.1/src/pyrsql/core/json/options.py +0 -31
  189. pyrsql-0.0.1/src/pyrsql/core/json/path.py +0 -35
  190. pyrsql-0.0.1/src/pyrsql/core/json/values.py +0 -72
  191. pyrsql-0.0.1/src/pyrsql/core/options.py +0 -416
  192. pyrsql-0.0.1/src/pyrsql/core/procedure_policy.py +0 -61
  193. pyrsql-0.0.1/src/pyrsql/integrations/fastapi/sqlalchemy.py +0 -251
  194. pyrsql-0.0.1/src/pyrsql/orms/base.py +0 -99
  195. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/coercion.py +0 -57
  196. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/json_path.py +0 -351
  197. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/orm.py +0 -65
  198. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/resolver.py +0 -202
  199. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/sorter.py +0 -173
  200. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/statement.py +0 -40
  201. pyrsql-0.0.1/src/pyrsql/orms/sqlalchemy/type_inference.py +0 -27
  202. pyrsql-0.0.1/src/pyrsql/parsing/ast.py +0 -61
  203. pyrsql-0.0.1/src/pyrsql/parsing/errors.py +0 -25
  204. pyrsql-0.0.1/src/pyrsql/parsing/limits.py +0 -15
  205. pyrsql-0.0.1/src/pyrsql/parsing/operators.py +0 -201
  206. pyrsql-0.0.1/src/pyrsql/parsing/source.py +0 -48
  207. pyrsql-0.0.1/src/pyrsql/selector/analyzer.py +0 -58
  208. pyrsql-0.0.1/src/pyrsql/selector/ast.py +0 -31
  209. pyrsql-0.0.1/src/pyrsql/selector/parser.py +0 -130
  210. pyrsql-0.0.1/src/pyrsql/selector/semantic.py +0 -34
  211. pyrsql-0.0.1/src/pyrsql/semantic/__init__.py +0 -23
  212. pyrsql-0.0.1/src/pyrsql/semantic/analyzer.py +0 -116
  213. pyrsql-0.0.1/src/pyrsql/semantic/ast.py +0 -35
  214. pyrsql-0.0.1/src/pyrsql/semantic/errors.py +0 -40
  215. pyrsql-0.0.1/src/pyrsql/sorting/analyzer.py +0 -74
  216. pyrsql-0.0.1/src/pyrsql/sorting/errors.py +0 -21
  217. pyrsql-0.0.1/src/pyrsql/sorting/limits.py +0 -15
  218. pyrsql-0.0.1/src/pyrsql/sorting/parser.py +0 -150
  219. pyrsql-0.0.1/src/pyrsql/sorting/semantic.py +0 -15
  220. pyrsql-0.0.1/tests/conftest.py +0 -31
  221. pyrsql-0.0.1/tests/fixtures/__init__.py +0 -1
  222. pyrsql-0.0.1/tests/fixtures/orms.py +0 -1
  223. pyrsql-0.0.1/tests/fixtures/query_samples.py +0 -1
  224. pyrsql-0.0.1/tests/fixtures/sqlalchemy_models.py +0 -1
  225. pyrsql-0.0.1/tests/functional/fastapi_sqlalchemy/test_integration.py +0 -126
  226. pyrsql-0.0.1/tests/functional/test_public_api.py +0 -80
  227. pyrsql-0.0.1/tests/integration/sqlalchemy/test_page_pipeline.py +0 -65
  228. pyrsql-0.0.1/tests/integration/sqlalchemy/test_sort_pipeline.py +0 -177
  229. pyrsql-0.0.1/tests/performance/test_semantic_bench.py +0 -36
  230. pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_config.py +0 -40
  231. pyrsql-0.0.1/tests/unit/adapters/fastapi_adapter/test_criteria.py +0 -83
  232. pyrsql-0.0.1/tests/unit/core/json/test_options.py +0 -32
  233. pyrsql-0.0.1/tests/unit/core/json/test_path.py +0 -20
  234. pyrsql-0.0.1/tests/unit/core/json/test_query.py +0 -28
  235. pyrsql-0.0.1/tests/unit/core/json/test_values.py +0 -21
  236. pyrsql-0.0.1/tests/unit/core/test_conversion.py +0 -109
  237. pyrsql-0.0.1/tests/unit/core/test_options.py +0 -160
  238. pyrsql-0.0.1/tests/unit/core/test_page.py +0 -99
  239. pyrsql-0.0.1/tests/unit/core/test_query.py +0 -87
  240. pyrsql-0.0.1/tests/unit/core/test_sort.py +0 -62
  241. pyrsql-0.0.1/tests/unit/integrations/fastapi_sqlalchemy/test_integration.py +0 -136
  242. pyrsql-0.0.1/tests/unit/orms/sqlalchemy/test_resolver.py +0 -187
  243. pyrsql-0.0.1/tests/unit/parsing/test_lexer.py +0 -78
  244. pyrsql-0.0.1/tests/unit/parsing/test_parser.py +0 -108
  245. pyrsql-0.0.1/tests/unit/selector/test_parser.py +0 -13
  246. pyrsql-0.0.1/tests/unit/semantic/test_analyzer.py +0 -93
  247. pyrsql-0.0.1/tests/unit/sorting/test_analyzer.py +0 -80
  248. pyrsql-0.0.1/tests/unit/sorting/test_parser.py +0 -81
  249. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/core/joins.py +0 -0
  250. {pyrsql-0.0.1 → pyrsql-0.0.3}/src/pyrsql/py.typed +0 -0
  251. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/functional/__init__.py +0 -0
  252. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/functional/fastapi_adapter/__init__.py +0 -0
  253. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/functional/fastapi_sqlalchemy/__init__.py +0 -0
  254. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/integration/__init__.py +0 -0
  255. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/integration/sqlalchemy/__init__.py +0 -0
  256. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/performance/__init__.py +0 -0
  257. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/adapters/fastapi_adapter/__init__.py +0 -0
  258. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/core/__init__.py +0 -0
  259. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/core/json/__init__.py +0 -0
  260. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/integrations/__init__.py +0 -0
  261. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/integrations/fastapi/__init__.py +0 -0
  262. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/integrations/fastapi_sqlalchemy/__init__.py +0 -0
  263. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/orms/__init__.py +0 -0
  264. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/orms/sqlalchemy/__init__.py +0 -0
  265. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/parsing/__init__.py +0 -0
  266. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/selector/__init__.py +0 -0
  267. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/semantic/__init__.py +0 -0
  268. {pyrsql-0.0.1 → pyrsql-0.0.3}/tests/unit/sorting/__init__.py +0 -0
pyrsql-0.0.3/PKG-INFO ADDED
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyrsql
3
+ Version: 0.0.3
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: Programming Language :: Python :: Free Threading
18
+ Classifier: Framework :: FastAPI
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Software Development :: Compilers
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ 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
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
+ [![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/)
42
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
43
+
44
+ pyrsql compiles RSQL query strings into ORM-specific statement objects
45
+ through a language frontend, semantic binding, and pluggable backend
46
+ lowering - making it easy to expose complex query capabilities in your API
47
+ 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
+ backend lowering are separate stages. Adding a new ORM backend means
58
+ implementing one interface (`ORM`), not rewriting the parser or query
59
+ language.
60
+
61
+ - **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies
62
+ - **Pluggable backends** - implement `compile_query` / `compile_sort` /
63
+ `compile_page_request` for any ORM
64
+ - **Pluggable framework adapters** - FastAPI today, Flask/Django tomorrow
65
+ - **Custom operators** - define your own RSQL operators with per-ORM lowering
66
+ - **Field policies** - whitelist, blacklist, aliases at global and per-model level
67
+ - **Type-safe** - strict mypy, Google-style docstrings, immutable value objects
68
+ - **Performance-oriented internals** - immutable `msgspec` models across the
69
+ core pipeline and configuration objects
70
+ - **Security-oriented request handling** - parser/sort limits, structural
71
+ allowlists/blocklists, sanitized FastAPI error payloads
72
+
73
+ ## Quickstart
74
+
75
+ ```bash
76
+ pip install pyrsql[sqlalchemy]
77
+ ```
78
+
79
+ ```python
80
+ import pyrsql
81
+ from sqlalchemy import select
82
+ from pyrsql.orms.sqlalchemy import SQLAlchemyORM
83
+
84
+ orm = SQLAlchemyORM()
85
+ stmt = select(User)
86
+
87
+ # Filter: name equals "demo", company name contains "acme"
88
+ stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
89
+
90
+ # Sort: by name ascending, then company name descending
91
+ stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
92
+
93
+ # Paginate: page 0, 25 items per page
94
+ stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
95
+ ```
96
+
97
+ ## Features
98
+
99
+ ### Query (filter)
100
+
101
+ - 20+ built-in operators: `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=`, `=in=`, `=out=`, `=like=`, `=ilike=`, `=bt=`, `=na=`, `=nn=`, etc.
102
+ - Logical composition: `;` (AND), `,` (OR)
103
+ - Grouping with parentheses
104
+ - Wildcard matching (`*demo*`) and case-insensitive markers (`^demo`)
105
+ - Strict equality mode (literal `*` and `^`)
106
+ - Configurable `LIKE` escape character
107
+ - `SELECT DISTINCT` support
108
+ - Configurable parser limits (query length, depth, argument count)
109
+
110
+ ### Sort
111
+
112
+ - Multi-field: `name,asc;company.name,desc,ic`
113
+ - Ignore-case modifier (`ic`)
114
+ - Function selectors: `@upper[name],asc`
115
+
116
+ ### Pagination
117
+
118
+ - Page-number + page-size API: `PageRequest.of(0, 25)`
119
+ - Offset + limit API: `PageRequest.from_offset(offset=50, limit=25)`
120
+ - Configurable max page size
121
+
122
+ ### Field Mapping & Access Control
123
+
124
+ - Aliases: `field_mapping={"username": "user.name"}`
125
+ - Whitelist/blacklist: `field_whitelist`, `field_blacklist`
126
+ - Per-model policies: `model_field_mapping`, `model_field_whitelist`, `model_field_blacklist`
127
+
128
+ ### Custom Predicates
129
+
130
+ Define custom operators with ORM-specific lowering:
131
+
132
+ ```python
133
+ from pyrsql import CustomPredicateDefinition, QueryOptions
134
+ from pyrsql.parsing.operators import ComparisonOperator
135
+
136
+ all_match = ComparisonOperator(name="all_match", spellings=("=all=",), ...)
137
+ options = QueryOptions(
138
+ custom_predicates={
139
+ "all_match": CustomPredicateDefinition(operator=all_match, argument_type=str),
140
+ },
141
+ )
142
+ ```
143
+
144
+ ### Value Conversion
145
+
146
+ - Built-in: `bool`, `int`, `float`, `Decimal`, `UUID`, `date`, `time`, `datetime`, `enum`
147
+ - Custom converters: `ValueConverterRegistry` + `with_converter`
148
+ - Field-scoped: `field_value_converters={"created_at": my_converter}`
149
+ - Model-scoped: `model_field_value_converters={MyModel: {"field": my_converter}}`
150
+
151
+ ### Join Hints
152
+
153
+ ```python
154
+ from pyrsql import QueryOptions
155
+ from pyrsql.core.joins import JoinHint
156
+
157
+ options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
158
+ ```
159
+
160
+ ### PostgreSQL JSON / JSONB
161
+
162
+ - **Whole-document**: direct `==`, `!=`, `=in=`, `=out=`, `=na=`, `=nn=` against JSONB columns
163
+ - **Nested path**: `jsonb_path_exists` via PostgreSQL `jsonpath`
164
+ - **Structured values**: arrays and objects passed as `jsonpath` vars
165
+ - **Temporal**: `JSONOptions(use_datetime=True)` for datetime-aware `jsonpath`
166
+ - **JSON sort**: text, integer, float, numeric, boolean, date, time, datetime
167
+ - **Custom function names**: `JSONOptions(path_exists_function=...)`
168
+
169
+ ### FastAPI Integration
170
+
171
+ ```python
172
+ from fastapi import Depends, FastAPI
173
+ from pyrsql.adapters.fastapi import criteria_dependency
174
+
175
+ app = FastAPI()
176
+ dependency = criteria_dependency()
177
+
178
+ @app.get("/items")
179
+ def list_items(criteria = Depends(dependency)):
180
+ ...
181
+ ```
182
+
183
+ - Auto-extracts `filter`, `sort`, `page`, `size` from query params
184
+ - `HTTP 422` with structured diagnostics on parse/semantic errors
185
+ - OpenAPI examples from configuration
186
+ - One-based paging support
187
+ - Custom query parameter names
188
+
189
+ ### FastAPI + SQLAlchemy Integration
190
+
191
+ ```python
192
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
193
+
194
+ integration = FastAPISQLAlchemyIntegration()
195
+
196
+ @app.get("/users")
197
+ def list_users(stmt = Depends(integration.select_dependency(User))):
198
+ return {"sql": str(stmt)}
199
+ ```
200
+
201
+ - `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
202
+ - Declarative `resource()` with auto-generated OpenAPI examples
203
+ - `applier_dependency` for custom base statements
204
+ - Compatible with both sync `Session` and async `AsyncSession` execution
205
+ - FastAPI parse and page-validation failures become structured `HTTP 400` payloads
206
+ - FastAPI semantic and backend integration failures become structured `HTTP 422` payloads
207
+
208
+ ### Concurrency and Validation
209
+
210
+ - Shared integration and ORM metadata caches are protected for free-threaded execution
211
+ - Async support is validated for adapter, ORM, and integration flows
212
+ - Dedicated async, free-threaded, and security test suites validate these flows
213
+
214
+ ## Documentation
215
+
216
+ Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsql/)**.
217
+
218
+ | Section | Description |
219
+ |---------|-------------|
220
+ | [Quickstart](https://wskr00.github.io/pyrsql/quickstart/) | One-minute primer |
221
+ | [Usage](https://wskr00.github.io/pyrsql/usage/query/) | Filter, sort, page, JSON, FastAPI, async flows, custom predicates |
222
+ | [API Reference](https://wskr00.github.io/pyrsql/reference/api/) | Auto-generated from docstrings |
223
+ | [Operators](https://wskr00.github.io/pyrsql/reference/operators/) | Complete operator table |
224
+ | [Options](https://wskr00.github.io/pyrsql/reference/options/) | QueryOptions, SortOptions, JSONOptions |
225
+ | [Architecture](https://wskr00.github.io/pyrsql/explanation/architecture/) | Pipeline, modules, design |
226
+ | [Extensibility](https://wskr00.github.io/pyrsql/explanation/extensibility/) | Adding backends and adapters |
227
+ | [Testing](https://wskr00.github.io/pyrsql/testing/) | Test layers, async, security, free-threaded validation |
228
+ | [Contributing](https://wskr00.github.io/pyrsql/contributing/) | Setup, workflow, standards |
229
+
230
+ ## Development Principles
231
+
232
+ - Object-oriented design
233
+ - SOLID principles
234
+ - Google Python Style Guide
235
+ - Strong typing
236
+ - ORM-neutral public API
237
+
238
+ ## License
239
+
240
+ [MIT](LICENSE).
pyrsql-0.0.3/README.md ADDED
@@ -0,0 +1,205 @@
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
+ [![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/)
7
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
+
9
+ pyrsql compiles RSQL query strings into ORM-specific statement objects
10
+ through a language frontend, semantic binding, and pluggable backend
11
+ lowering - making it easy to expose complex query capabilities in your API
12
+ 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
+ backend lowering are separate stages. Adding a new ORM backend means
23
+ implementing one interface (`ORM`), not rewriting the parser or query
24
+ language.
25
+
26
+ - **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies
27
+ - **Pluggable backends** - implement `compile_query` / `compile_sort` /
28
+ `compile_page_request` for any ORM
29
+ - **Pluggable framework adapters** - FastAPI today, Flask/Django tomorrow
30
+ - **Custom operators** - define your own RSQL operators with per-ORM lowering
31
+ - **Field policies** - whitelist, blacklist, aliases at global and per-model level
32
+ - **Type-safe** - strict mypy, Google-style docstrings, immutable value objects
33
+ - **Performance-oriented internals** - immutable `msgspec` models across the
34
+ core pipeline and configuration objects
35
+ - **Security-oriented request handling** - parser/sort limits, structural
36
+ allowlists/blocklists, sanitized FastAPI error payloads
37
+
38
+ ## Quickstart
39
+
40
+ ```bash
41
+ pip install pyrsql[sqlalchemy]
42
+ ```
43
+
44
+ ```python
45
+ import pyrsql
46
+ from sqlalchemy import select
47
+ from pyrsql.orms.sqlalchemy import SQLAlchemyORM
48
+
49
+ orm = SQLAlchemyORM()
50
+ stmt = select(User)
51
+
52
+ # Filter: name equals "demo", company name contains "acme"
53
+ stmt = pyrsql.parse("name==demo;company.name==acme*").apply(stmt, User, orm=orm)
54
+
55
+ # Sort: by name ascending, then company name descending
56
+ stmt = pyrsql.Sort.parse("name,asc;company.name,desc").apply(stmt, User, orm=orm)
57
+
58
+ # Paginate: page 0, 25 items per page
59
+ stmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)
60
+ ```
61
+
62
+ ## Features
63
+
64
+ ### Query (filter)
65
+
66
+ - 20+ built-in operators: `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=`, `=in=`, `=out=`, `=like=`, `=ilike=`, `=bt=`, `=na=`, `=nn=`, etc.
67
+ - Logical composition: `;` (AND), `,` (OR)
68
+ - Grouping with parentheses
69
+ - Wildcard matching (`*demo*`) and case-insensitive markers (`^demo`)
70
+ - Strict equality mode (literal `*` and `^`)
71
+ - Configurable `LIKE` escape character
72
+ - `SELECT DISTINCT` support
73
+ - Configurable parser limits (query length, depth, argument count)
74
+
75
+ ### Sort
76
+
77
+ - Multi-field: `name,asc;company.name,desc,ic`
78
+ - Ignore-case modifier (`ic`)
79
+ - Function selectors: `@upper[name],asc`
80
+
81
+ ### Pagination
82
+
83
+ - Page-number + page-size API: `PageRequest.of(0, 25)`
84
+ - Offset + limit API: `PageRequest.from_offset(offset=50, limit=25)`
85
+ - Configurable max page size
86
+
87
+ ### Field Mapping & Access Control
88
+
89
+ - Aliases: `field_mapping={"username": "user.name"}`
90
+ - Whitelist/blacklist: `field_whitelist`, `field_blacklist`
91
+ - Per-model policies: `model_field_mapping`, `model_field_whitelist`, `model_field_blacklist`
92
+
93
+ ### Custom Predicates
94
+
95
+ Define custom operators with ORM-specific lowering:
96
+
97
+ ```python
98
+ from pyrsql import CustomPredicateDefinition, QueryOptions
99
+ from pyrsql.parsing.operators import ComparisonOperator
100
+
101
+ all_match = ComparisonOperator(name="all_match", spellings=("=all=",), ...)
102
+ options = QueryOptions(
103
+ custom_predicates={
104
+ "all_match": CustomPredicateDefinition(operator=all_match, argument_type=str),
105
+ },
106
+ )
107
+ ```
108
+
109
+ ### Value Conversion
110
+
111
+ - Built-in: `bool`, `int`, `float`, `Decimal`, `UUID`, `date`, `time`, `datetime`, `enum`
112
+ - Custom converters: `ValueConverterRegistry` + `with_converter`
113
+ - Field-scoped: `field_value_converters={"created_at": my_converter}`
114
+ - Model-scoped: `model_field_value_converters={MyModel: {"field": my_converter}}`
115
+
116
+ ### Join Hints
117
+
118
+ ```python
119
+ from pyrsql import QueryOptions
120
+ from pyrsql.core.joins import JoinHint
121
+
122
+ options = QueryOptions(join_hints={"User.company": JoinHint.LEFT})
123
+ ```
124
+
125
+ ### PostgreSQL JSON / JSONB
126
+
127
+ - **Whole-document**: direct `==`, `!=`, `=in=`, `=out=`, `=na=`, `=nn=` against JSONB columns
128
+ - **Nested path**: `jsonb_path_exists` via PostgreSQL `jsonpath`
129
+ - **Structured values**: arrays and objects passed as `jsonpath` vars
130
+ - **Temporal**: `JSONOptions(use_datetime=True)` for datetime-aware `jsonpath`
131
+ - **JSON sort**: text, integer, float, numeric, boolean, date, time, datetime
132
+ - **Custom function names**: `JSONOptions(path_exists_function=...)`
133
+
134
+ ### FastAPI Integration
135
+
136
+ ```python
137
+ from fastapi import Depends, FastAPI
138
+ from pyrsql.adapters.fastapi import criteria_dependency
139
+
140
+ app = FastAPI()
141
+ dependency = criteria_dependency()
142
+
143
+ @app.get("/items")
144
+ def list_items(criteria = Depends(dependency)):
145
+ ...
146
+ ```
147
+
148
+ - Auto-extracts `filter`, `sort`, `page`, `size` from query params
149
+ - `HTTP 422` with structured diagnostics on parse/semantic errors
150
+ - OpenAPI examples from configuration
151
+ - One-based paging support
152
+ - Custom query parameter names
153
+
154
+ ### FastAPI + SQLAlchemy Integration
155
+
156
+ ```python
157
+ from pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration
158
+
159
+ integration = FastAPISQLAlchemyIntegration()
160
+
161
+ @app.get("/users")
162
+ def list_users(stmt = Depends(integration.select_dependency(User))):
163
+ return {"sql": str(stmt)}
164
+ ```
165
+
166
+ - `select_dependency`, `count_select_dependency`, `paginated_select_dependency`
167
+ - Declarative `resource()` with auto-generated OpenAPI examples
168
+ - `applier_dependency` for custom base statements
169
+ - Compatible with both sync `Session` and async `AsyncSession` execution
170
+ - FastAPI parse and page-validation failures become structured `HTTP 400` payloads
171
+ - FastAPI semantic and backend integration failures become structured `HTTP 422` payloads
172
+
173
+ ### Concurrency and Validation
174
+
175
+ - Shared integration and ORM metadata caches are protected for free-threaded execution
176
+ - Async support is validated for adapter, ORM, and integration flows
177
+ - Dedicated async, free-threaded, and security test suites validate these flows
178
+
179
+ ## Documentation
180
+
181
+ Full documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsql/)**.
182
+
183
+ | Section | Description |
184
+ |---------|-------------|
185
+ | [Quickstart](https://wskr00.github.io/pyrsql/quickstart/) | One-minute primer |
186
+ | [Usage](https://wskr00.github.io/pyrsql/usage/query/) | Filter, sort, page, JSON, FastAPI, async flows, custom predicates |
187
+ | [API Reference](https://wskr00.github.io/pyrsql/reference/api/) | Auto-generated from docstrings |
188
+ | [Operators](https://wskr00.github.io/pyrsql/reference/operators/) | Complete operator table |
189
+ | [Options](https://wskr00.github.io/pyrsql/reference/options/) | QueryOptions, SortOptions, JSONOptions |
190
+ | [Architecture](https://wskr00.github.io/pyrsql/explanation/architecture/) | Pipeline, modules, design |
191
+ | [Extensibility](https://wskr00.github.io/pyrsql/explanation/extensibility/) | Adding backends and adapters |
192
+ | [Testing](https://wskr00.github.io/pyrsql/testing/) | Test layers, async, security, free-threaded validation |
193
+ | [Contributing](https://wskr00.github.io/pyrsql/contributing/) | Setup, workflow, standards |
194
+
195
+ ## Development Principles
196
+
197
+ - Object-oriented design
198
+ - SOLID principles
199
+ - Google Python Style Guide
200
+ - Strong typing
201
+ - ORM-neutral public API
202
+
203
+ ## License
204
+
205
+ [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.parsing`, `pyrsql.semantic`, `pyrsql.sorting`
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