sqlspec 0.47.0__cp314-cp314-win_amd64.whl
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.
- f68e0789eb443ecb1c2c__mypyc.cp314-win_amd64.pyd +0 -0
- sqlspec/__init__.py +167 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_typing.py +714 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +13 -0
- sqlspec/adapters/adbc/_typing.py +106 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1280 -0
- sqlspec/adapters/adbc/config.py +378 -0
- sqlspec/adapters/adbc/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/core.py +922 -0
- sqlspec/adapters/adbc/data_dictionary.py +339 -0
- sqlspec/adapters/adbc/driver.py +534 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +534 -0
- sqlspec/adapters/adbc/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/type_converter.py +142 -0
- sqlspec/adapters/aiomysql/__init__.py +21 -0
- sqlspec/adapters/aiomysql/_typing.py +137 -0
- sqlspec/adapters/aiomysql/adk/__init__.py +5 -0
- sqlspec/adapters/aiomysql/adk/store.py +678 -0
- sqlspec/adapters/aiomysql/config.py +305 -0
- sqlspec/adapters/aiomysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiomysql/core.py +536 -0
- sqlspec/adapters/aiomysql/data_dictionary.py +121 -0
- sqlspec/adapters/aiomysql/driver.py +386 -0
- sqlspec/adapters/aiomysql/events/__init__.py +5 -0
- sqlspec/adapters/aiomysql/events/store.py +104 -0
- sqlspec/adapters/aiomysql/litestar/__init__.py +5 -0
- sqlspec/adapters/aiomysql/litestar/store.py +314 -0
- sqlspec/adapters/aiosqlite/__init__.py +26 -0
- sqlspec/adapters/aiosqlite/_typing.py +109 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +829 -0
- sqlspec/adapters/aiosqlite/config.py +315 -0
- sqlspec/adapters/aiosqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +202 -0
- sqlspec/adapters/aiosqlite/driver.py +311 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/pool.py +734 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +113 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +644 -0
- sqlspec/adapters/asyncmy/config.py +307 -0
- sqlspec/adapters/asyncmy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncmy/core.py +538 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
- sqlspec/adapters/asyncmy/driver.py +391 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +26 -0
- sqlspec/adapters/asyncpg/_typing.py +103 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +483 -0
- sqlspec/adapters/asyncpg/config.py +575 -0
- sqlspec/adapters/asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncpg/core.py +480 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +157 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/_hub.py +181 -0
- sqlspec/adapters/asyncpg/events/backend.py +210 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +15 -0
- sqlspec/adapters/bigquery/_typing.py +108 -0
- sqlspec/adapters/bigquery/config.py +362 -0
- sqlspec/adapters/bigquery/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/core.py +768 -0
- sqlspec/adapters/bigquery/data_dictionary.py +120 -0
- sqlspec/adapters/bigquery/driver.py +542 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +26 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +73 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +465 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +248 -0
- sqlspec/adapters/cockroach_asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +110 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +142 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +39 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +137 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +1039 -0
- sqlspec/adapters/cockroach_psycopg/config.py +511 -0
- sqlspec/adapters/cockroach_psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +220 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +273 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +327 -0
- sqlspec/adapters/duckdb/__init__.py +29 -0
- sqlspec/adapters/duckdb/_typing.py +104 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +935 -0
- sqlspec/adapters/duckdb/config.py +386 -0
- sqlspec/adapters/duckdb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/core.py +332 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +426 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/pool.py +350 -0
- sqlspec/adapters/duckdb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mysqlconnector/__init__.py +39 -0
- sqlspec/adapters/mysqlconnector/_typing.py +186 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1183 -0
- sqlspec/adapters/mysqlconnector/config.py +421 -0
- sqlspec/adapters/mysqlconnector/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/mysqlconnector/core.py +472 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +230 -0
- sqlspec/adapters/mysqlconnector/driver.py +516 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +39 -0
- sqlspec/adapters/oracledb/_json_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_json_handlers.py +196 -0
- sqlspec/adapters/oracledb/_param_types.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_param_types.py +46 -0
- sqlspec/adapters/oracledb/_typing.py +258 -0
- sqlspec/adapters/oracledb/_uuid_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +163 -0
- sqlspec/adapters/oracledb/_vector_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_vector_handlers.py +228 -0
- sqlspec/adapters/oracledb/adk/__init__.py +21 -0
- sqlspec/adapters/oracledb/adk/store.py +2453 -0
- sqlspec/adapters/oracledb/config.py +575 -0
- sqlspec/adapters/oracledb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/core.py +820 -0
- sqlspec/adapters/oracledb/data_dictionary.py +404 -0
- sqlspec/adapters/oracledb/driver.py +1277 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/_hub.py +345 -0
- sqlspec/adapters/oracledb/events/backend.py +300 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +539 -0
- sqlspec/adapters/oracledb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +18 -0
- sqlspec/adapters/psqlpy/_typing.py +121 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +591 -0
- sqlspec/adapters/psqlpy/config.py +376 -0
- sqlspec/adapters/psqlpy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/core.py +694 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +121 -0
- sqlspec/adapters/psqlpy/driver.py +411 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/_hub.py +204 -0
- sqlspec/adapters/psqlpy/events/backend.py +210 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +38 -0
- sqlspec/adapters/psycopg/_typing.py +218 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1106 -0
- sqlspec/adapters/psycopg/config.py +695 -0
- sqlspec/adapters/psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/core.py +520 -0
- sqlspec/adapters/psycopg/data_dictionary.py +278 -0
- sqlspec/adapters/psycopg/driver.py +1033 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/_hub.py +388 -0
- sqlspec/adapters/psycopg/events/backend.py +398 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/psycopg/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +92 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +657 -0
- sqlspec/adapters/pymysql/config.py +176 -0
- sqlspec/adapters/pymysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/core.py +469 -0
- sqlspec/adapters/pymysql/data_dictionary.py +120 -0
- sqlspec/adapters/pymysql/driver.py +271 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/pool.py +184 -0
- sqlspec/adapters/spanner/__init__.py +33 -0
- sqlspec/adapters/spanner/_typing.py +102 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +758 -0
- sqlspec/adapters/spanner/config.py +355 -0
- sqlspec/adapters/spanner/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/core.py +263 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/driver.py +407 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/type_converter.py +342 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +123 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +992 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/core.py +357 -0
- sqlspec/adapters/sqlite/data_dictionary.py +198 -0
- sqlspec/adapters/sqlite/driver.py +527 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/pool.py +237 -0
- sqlspec/adapters/sqlite/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +832 -0
- sqlspec/builder/__init__.py +181 -0
- sqlspec/builder/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_base.py +1071 -0
- sqlspec/builder/_column.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_ddl.py +1691 -0
- sqlspec/builder/_delete.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_dml.py +386 -0
- sqlspec/builder/_explain.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_factory.py +1884 -0
- sqlspec/builder/_insert.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_insert.py +405 -0
- sqlspec/builder/_join.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_join.py +489 -0
- sqlspec/builder/_merge.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_merge.py +823 -0
- sqlspec/builder/_parsing_utils.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_parsing_utils.py +295 -0
- sqlspec/builder/_select.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_select.py +1666 -0
- sqlspec/builder/_temporal.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_temporal.py +167 -0
- sqlspec/builder/_update.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_distance.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_vector_distance.py +330 -0
- sqlspec/cli.py +1095 -0
- sqlspec/config.py +2383 -0
- sqlspec/core/__init__.py +372 -0
- sqlspec/core/_correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/_pagination.py +42 -0
- sqlspec/core/_pool.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_pool.py +76 -0
- sqlspec/core/cache.cp314-win_amd64.pyd +0 -0
- sqlspec/core/cache.py +1085 -0
- sqlspec/core/compiler.cp314-win_amd64.pyd +0 -0
- sqlspec/core/compiler.py +1090 -0
- sqlspec/core/config_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/core/config_runtime.py +174 -0
- sqlspec/core/explain.cp314-win_amd64.pyd +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cp314-win_amd64.pyd +0 -0
- sqlspec/core/filters.py +969 -0
- sqlspec/core/hashing.cp314-win_amd64.pyd +0 -0
- sqlspec/core/hashing.py +266 -0
- sqlspec/core/metrics.cp314-win_amd64.pyd +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +72 -0
- sqlspec/core/parameters/_alignment.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_alignment.py +283 -0
- sqlspec/core/parameters/_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_converter.py +554 -0
- sqlspec/core/parameters/_processor.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_processor.py +1182 -0
- sqlspec/core/parameters/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_transformers.py +324 -0
- sqlspec/core/parameters/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_types.py +536 -0
- sqlspec/core/parameters/_validator.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_validator.py +171 -0
- sqlspec/core/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/core/pipeline.py +333 -0
- sqlspec/core/query_modifiers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/query_modifiers.py +508 -0
- sqlspec/core/result/__init__.py +25 -0
- sqlspec/core/result/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_base.py +1232 -0
- sqlspec/core/result/_io.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/splitter.py +1021 -0
- sqlspec/core/sqlcommenter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/sqlcommenter.py +249 -0
- sqlspec/core/stack.cp314-win_amd64.pyd +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cp314-win_amd64.pyd +0 -0
- sqlspec/core/statement.py +1865 -0
- sqlspec/core/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/type_converter.py +340 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_loader.py +138 -0
- sqlspec/data_dictionary/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +81 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +54 -0
- sqlspec/data_dictionary/dialects/duckdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +53 -0
- sqlspec/data_dictionary/dialects/oracle.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +197 -0
- sqlspec/data_dictionary/dialects/postgres.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +69 -0
- sqlspec/data_dictionary/dialects/spanner.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +59 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/dialects/__init__.py +22 -0
- sqlspec/dialects/_compat.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/_compat.py +14 -0
- sqlspec/dialects/postgres/__init__.py +9 -0
- sqlspec/dialects/postgres/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_generators.py +57 -0
- sqlspec/dialects/postgres/_operators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_operators.py +81 -0
- sqlspec/dialects/postgres/_paradedb.py +50 -0
- sqlspec/dialects/postgres/_pgvector.py +36 -0
- sqlspec/dialects/spanner/__init__.py +6 -0
- sqlspec/dialects/spanner/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/spanner/_generators.py +206 -0
- sqlspec/dialects/spanner/_spangres.py +77 -0
- sqlspec/dialects/spanner/_spanner.py +179 -0
- sqlspec/driver/__init__.py +49 -0
- sqlspec/driver/_async.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_async.py +1830 -0
- sqlspec/driver/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_common.py +2292 -0
- sqlspec/driver/_exception_handler.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_exception_handler.py +108 -0
- sqlspec/driver/_query_cache.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_query_cache.py +96 -0
- sqlspec/driver/_sql_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sql_helpers.py +139 -0
- sqlspec/driver/_storage_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_storage_helpers.py +153 -0
- sqlspec/driver/_sync.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sync.py +1817 -0
- sqlspec/exceptions.cp314-win_amd64.pyd +0 -0
- sqlspec/exceptions.py +480 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +84 -0
- sqlspec/extensions/adk/_config_utils.py +199 -0
- sqlspec/extensions/adk/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/_types.py +41 -0
- sqlspec/extensions/adk/artifact/__init__.py +57 -0
- sqlspec/extensions/adk/artifact/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/artifact/_types.py +32 -0
- sqlspec/extensions/adk/artifact/service.py +508 -0
- sqlspec/extensions/adk/artifact/store.py +361 -0
- sqlspec/extensions/adk/converters.py +212 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +225 -0
- sqlspec/extensions/adk/memory/service.py +316 -0
- sqlspec/extensions/adk/memory/store.py +525 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +184 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +279 -0
- sqlspec/extensions/adk/store.py +590 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +462 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +22 -0
- sqlspec/extensions/fastapi/extension.py +391 -0
- sqlspec/extensions/fastapi/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/fastapi/providers.py +712 -0
- sqlspec/extensions/flask/__init__.py +38 -0
- sqlspec/extensions/flask/_state.py +87 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +539 -0
- sqlspec/extensions/litestar/__init__.py +31 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +1066 -0
- sqlspec/extensions/litestar/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/litestar/providers.py +784 -0
- sqlspec/extensions/litestar/store.py +298 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/sanic/__init__.py +19 -0
- sqlspec/extensions/sanic/_state.py +43 -0
- sqlspec/extensions/sanic/_utils.py +127 -0
- sqlspec/extensions/sanic/extension.py +647 -0
- sqlspec/extensions/starlette/__init__.py +22 -0
- sqlspec/extensions/starlette/_state.py +42 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +374 -0
- sqlspec/extensions/starlette/middleware.py +281 -0
- sqlspec/loader.cp314-win_amd64.pyd +0 -0
- sqlspec/loader.py +727 -0
- sqlspec/migrations/__init__.py +39 -0
- sqlspec/migrations/base.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/base.py +862 -0
- sqlspec/migrations/commands.py +2151 -0
- sqlspec/migrations/context.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/runner.py +1195 -0
- sqlspec/migrations/squash.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/squash.py +490 -0
- sqlspec/migrations/templates.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/tracker.py +792 -0
- sqlspec/migrations/utils.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/validation.py +359 -0
- sqlspec/migrations/version.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +57 -0
- sqlspec/observability/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_config.py +364 -0
- sqlspec/observability/_diagnostics.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_dispatcher.py +200 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_observer.py +361 -0
- sqlspec/observability/_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_runtime.py +461 -0
- sqlspec/observability/_sampling.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +955 -0
- sqlspec/py.typed +0 -0
- sqlspec/service.py +433 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_arrow_payload.py +68 -0
- sqlspec/storage/_paths.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/_paths.py +58 -0
- sqlspec/storage/_utils.py +46 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/backends/base.py +374 -0
- sqlspec/storage/backends/fsspec.py +574 -0
- sqlspec/storage/backends/local.py +468 -0
- sqlspec/storage/backends/obstore.py +956 -0
- sqlspec/storage/errors.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/errors.py +102 -0
- sqlspec/storage/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/pipeline.py +628 -0
- sqlspec/storage/registry.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/registry.py +329 -0
- sqlspec/typing.py +405 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +384 -0
- sqlspec/utils/config_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/config_tools.py +314 -0
- sqlspec/utils/correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/deprecation.py +157 -0
- sqlspec/utils/dispatch.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/dispatch.py +101 -0
- sqlspec/utils/fixtures.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/fixtures.py +260 -0
- sqlspec/utils/logging.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/logging.py +251 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/portal.py +377 -0
- sqlspec/utils/schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/schema.py +1040 -0
- sqlspec/utils/serializers/__init__.py +30 -0
- sqlspec/utils/serializers/_json.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_json.py +415 -0
- sqlspec/utils/serializers/_numpy.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_numpy.py +65 -0
- sqlspec/utils/serializers/_schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_schema.py +285 -0
- sqlspec/utils/singleton.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/sync_tools.py +316 -0
- sqlspec/utils/text.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/text.py +109 -0
- sqlspec/utils/type_converters.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_converters.py +216 -0
- sqlspec/utils/type_guards.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_guards.py +1508 -0
- sqlspec/utils/uuids.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/uuids.py +241 -0
- sqlspec-0.47.0.dist-info/METADATA +202 -0
- sqlspec-0.47.0.dist-info/RECORD +621 -0
- sqlspec-0.47.0.dist-info/WHEEL +4 -0
- sqlspec-0.47.0.dist-info/entry_points.txt +6 -0
- sqlspec-0.47.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
"""Application dependency providers for FastAPI filter injection.
|
|
2
|
+
|
|
3
|
+
This module provides filter dependency injection for FastAPI routes, allowing
|
|
4
|
+
automatic parsing of query parameters into SQLSpec filter objects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import datetime
|
|
8
|
+
import inspect
|
|
9
|
+
from collections.abc import Callable, Mapping
|
|
10
|
+
from functools import partial
|
|
11
|
+
from types import GenericAlias
|
|
12
|
+
from typing import Annotated, Any, Literal, NamedTuple, cast
|
|
13
|
+
from uuid import UUID
|
|
14
|
+
|
|
15
|
+
from fastapi import Depends, Query
|
|
16
|
+
from fastapi.exceptions import RequestValidationError
|
|
17
|
+
from typing_extensions import NotRequired, TypedDict
|
|
18
|
+
|
|
19
|
+
from sqlspec.core import (
|
|
20
|
+
BeforeAfterFilter,
|
|
21
|
+
FilterTypes,
|
|
22
|
+
InCollectionFilter,
|
|
23
|
+
LimitOffsetFilter,
|
|
24
|
+
NotInCollectionFilter,
|
|
25
|
+
NotNullFilter,
|
|
26
|
+
NullFilter,
|
|
27
|
+
OrderByFilter,
|
|
28
|
+
SearchFilter,
|
|
29
|
+
)
|
|
30
|
+
from sqlspec.utils.text import camelize
|
|
31
|
+
|
|
32
|
+
__all__ = (
|
|
33
|
+
"DEPENDENCY_DEFAULTS",
|
|
34
|
+
"BooleanOrNone",
|
|
35
|
+
"DTorNone",
|
|
36
|
+
"DependencyDefaults",
|
|
37
|
+
"FieldNameType",
|
|
38
|
+
"FilterConfig",
|
|
39
|
+
"HashableType",
|
|
40
|
+
"HashableValue",
|
|
41
|
+
"IntOrNone",
|
|
42
|
+
"SortField",
|
|
43
|
+
"SortOrder",
|
|
44
|
+
"SortOrderOrNone",
|
|
45
|
+
"StringOrNone",
|
|
46
|
+
"UuidOrNone",
|
|
47
|
+
"dep_cache",
|
|
48
|
+
"provide_filters",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
DTorNone = datetime.datetime | None
|
|
52
|
+
StringOrNone = str | None
|
|
53
|
+
UuidOrNone = UUID | None
|
|
54
|
+
IntOrNone = int | None
|
|
55
|
+
BooleanOrNone = bool | None
|
|
56
|
+
SortOrder = Literal["asc", "desc"]
|
|
57
|
+
SortOrderOrNone = SortOrder | None
|
|
58
|
+
SortField = str | set[str] | list[str]
|
|
59
|
+
HashableValue = str | int | float | bool | None
|
|
60
|
+
HashableType = HashableValue | tuple[Any, ...] | tuple[tuple[str, Any], ...] | tuple[HashableValue, ...]
|
|
61
|
+
_FILTER_CONFIG_KEYS = frozenset({
|
|
62
|
+
"id_filter",
|
|
63
|
+
"created_at",
|
|
64
|
+
"updated_at",
|
|
65
|
+
"pagination_type",
|
|
66
|
+
"search",
|
|
67
|
+
"sort_field",
|
|
68
|
+
"not_in_fields",
|
|
69
|
+
"in_fields",
|
|
70
|
+
"null_fields",
|
|
71
|
+
"not_null_fields",
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DependencyDefaults:
|
|
76
|
+
"""Default values for dependency generation."""
|
|
77
|
+
|
|
78
|
+
CREATED_FILTER_DEPENDENCY_KEY: str = "created_filter"
|
|
79
|
+
ID_FILTER_DEPENDENCY_KEY: str = "id_filter"
|
|
80
|
+
LIMIT_OFFSET_FILTER_DEPENDENCY_KEY: str = "limit_offset_filter"
|
|
81
|
+
UPDATED_FILTER_DEPENDENCY_KEY: str = "updated_filter"
|
|
82
|
+
ORDER_BY_FILTER_DEPENDENCY_KEY: str = "order_by_filter"
|
|
83
|
+
SEARCH_FILTER_DEPENDENCY_KEY: str = "search_filter"
|
|
84
|
+
DEFAULT_PAGINATION_SIZE: int = 20
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
DEPENDENCY_DEFAULTS = DependencyDefaults()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class FieldNameType(NamedTuple):
|
|
91
|
+
"""Type for field name and associated type information for filter configuration."""
|
|
92
|
+
|
|
93
|
+
name: str
|
|
94
|
+
"""Name of the field to filter on."""
|
|
95
|
+
type_hint: type[Any] = str
|
|
96
|
+
"""Type of the filter value. Defaults to str."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class _SortFieldResolution(NamedTuple):
|
|
100
|
+
default_field: str
|
|
101
|
+
default_query_value: str
|
|
102
|
+
allowed_fields: frozenset[str]
|
|
103
|
+
inbound_aliases: dict[str, str]
|
|
104
|
+
field_display_names: dict[str, str]
|
|
105
|
+
allowed_display_names: tuple[str, ...]
|
|
106
|
+
|
|
107
|
+
def normalize(self, value: str | None) -> str | None:
|
|
108
|
+
if value is None:
|
|
109
|
+
return self.default_field
|
|
110
|
+
return self.inbound_aliases.get(value)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class FilterConfig(TypedDict):
|
|
114
|
+
"""Configuration for generated FastAPI filter dependencies.
|
|
115
|
+
|
|
116
|
+
All keys are optional. A filter dependency is created only for each enabled
|
|
117
|
+
key. Field names are SQL-facing allowlist values; generated query parameter
|
|
118
|
+
names and order-by aliases remain API-facing.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
id_filter: NotRequired[type[UUID | int | str]]
|
|
122
|
+
"""Type of ID filter to enable. When set, creates an ``ids`` collection filter."""
|
|
123
|
+
id_field: NotRequired[str]
|
|
124
|
+
"""SQL-facing field name for ID filtering. Defaults to ``"id"``."""
|
|
125
|
+
sort_field: NotRequired[SortField]
|
|
126
|
+
"""Allowed SQL-facing field or fields for ``orderBy`` sorting."""
|
|
127
|
+
sort_field_aliases: NotRequired[dict[str, str]]
|
|
128
|
+
"""Additional API-facing ``orderBy`` aliases mapped to configured ``sort_field`` values."""
|
|
129
|
+
sort_field_camelize: NotRequired[bool]
|
|
130
|
+
"""Whether to accept camel-case aliases for configured sort fields. Defaults to ``True``."""
|
|
131
|
+
sort_order: NotRequired[SortOrder]
|
|
132
|
+
"""Default sort order. Defaults to ``"desc"``."""
|
|
133
|
+
pagination_type: NotRequired[Literal["limit_offset"]]
|
|
134
|
+
"""Pagination strategy to enable. Currently supports ``"limit_offset"``."""
|
|
135
|
+
pagination_size: NotRequired[int]
|
|
136
|
+
"""Default page size for limit/offset pagination."""
|
|
137
|
+
search: NotRequired[str | set[str]]
|
|
138
|
+
"""SQL-facing field or fields to search. Strings may be comma-separated."""
|
|
139
|
+
search_ignore_case: NotRequired[bool]
|
|
140
|
+
"""Whether search filtering is case-insensitive. Defaults to ``False``."""
|
|
141
|
+
created_at: NotRequired[bool]
|
|
142
|
+
"""Whether to enable ``created_at`` before/after range filtering."""
|
|
143
|
+
updated_at: NotRequired[bool]
|
|
144
|
+
"""Whether to enable ``updated_at`` before/after range filtering."""
|
|
145
|
+
not_in_fields: NotRequired[FieldNameType | set[FieldNameType] | list[str | FieldNameType]]
|
|
146
|
+
"""Field or fields that support ``NOT IN`` collection filtering."""
|
|
147
|
+
in_fields: NotRequired[FieldNameType | set[FieldNameType] | list[str | FieldNameType]]
|
|
148
|
+
"""Field or fields that support ``IN`` collection filtering."""
|
|
149
|
+
null_fields: NotRequired[str | set[str]]
|
|
150
|
+
"""Field or fields that support ``IS NULL`` filtering."""
|
|
151
|
+
not_null_fields: NotRequired[str | set[str]]
|
|
152
|
+
"""Field or fields that support ``IS NOT NULL`` filtering."""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class DependencyCache:
|
|
156
|
+
"""Simple dependency cache to memoize dynamically generated dependencies."""
|
|
157
|
+
|
|
158
|
+
def __init__(self) -> None:
|
|
159
|
+
self.dependencies: dict[int, Callable[..., list[FilterTypes]]] = {}
|
|
160
|
+
|
|
161
|
+
def add_dependencies(self, key: int, dependencies: "Callable[..., list[FilterTypes]]") -> None:
|
|
162
|
+
"""Add dependencies to cache.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
key: Cache key (hash of config).
|
|
166
|
+
dependencies: Dependency callable to cache.
|
|
167
|
+
"""
|
|
168
|
+
self.dependencies[key] = dependencies
|
|
169
|
+
|
|
170
|
+
def get_dependencies(self, key: int) -> "Callable[..., list[FilterTypes]] | None":
|
|
171
|
+
"""Get dependencies from cache.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
key: Cache key (hash of config).
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Cached dependency callable or None if not found.
|
|
178
|
+
"""
|
|
179
|
+
return self.dependencies.get(key)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
dep_cache = DependencyCache()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def provide_filters(
|
|
186
|
+
config: FilterConfig, dep_defaults: DependencyDefaults = DEPENDENCY_DEFAULTS
|
|
187
|
+
) -> "Callable[..., list[FilterTypes]]":
|
|
188
|
+
"""Create FastAPI dependency provider for filters based on configuration.
|
|
189
|
+
|
|
190
|
+
This function dynamically generates a FastAPI dependency function that parses
|
|
191
|
+
query parameters into SQLSpec filter objects.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
config: Filter configuration specifying which filters to enable.
|
|
195
|
+
dep_defaults: Dependency defaults for filter configuration.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
A FastAPI dependency callable that returns list of filters.
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
from fastapi import Depends, FastAPI
|
|
202
|
+
from sqlspec.extensions.fastapi import SQLSpecPlugin, FilterConfig
|
|
203
|
+
|
|
204
|
+
app = FastAPI()
|
|
205
|
+
db_ext = SQLSpecPlugin(sql, app)
|
|
206
|
+
|
|
207
|
+
@app.get("/users")
|
|
208
|
+
async def list_users(
|
|
209
|
+
filters = Depends(
|
|
210
|
+
db_ext.provide_filters({
|
|
211
|
+
"id_filter": UUID,
|
|
212
|
+
"search": "name,email",
|
|
213
|
+
"pagination_type": "limit_offset",
|
|
214
|
+
})
|
|
215
|
+
),
|
|
216
|
+
):
|
|
217
|
+
stmt = sql("SELECT * FROM users")
|
|
218
|
+
for filter in filters:
|
|
219
|
+
stmt = filter.append_to_statement(stmt)
|
|
220
|
+
result = await db.execute(stmt)
|
|
221
|
+
return result.all()
|
|
222
|
+
"""
|
|
223
|
+
if not _has_filter_config(config):
|
|
224
|
+
return _empty_filter_list
|
|
225
|
+
|
|
226
|
+
cache_key = hash(_make_hashable(config))
|
|
227
|
+
|
|
228
|
+
cached_dep = dep_cache.get_dependencies(cache_key)
|
|
229
|
+
if cached_dep is not None:
|
|
230
|
+
return cached_dep
|
|
231
|
+
|
|
232
|
+
dep = _create_filter_aggregate_function(config, dep_defaults)
|
|
233
|
+
dep_cache.add_dependencies(cache_key, dep)
|
|
234
|
+
return dep
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _create_filter_aggregate_function(
|
|
238
|
+
config: FilterConfig, dep_defaults: DependencyDefaults = DEPENDENCY_DEFAULTS
|
|
239
|
+
) -> "Callable[..., list[FilterTypes]]":
|
|
240
|
+
"""Create a FastAPI dependency function that aggregates multiple filter dependencies.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
config: Filter configuration.
|
|
244
|
+
dep_defaults: Dependency defaults.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
A FastAPI dependency function that aggregates multiple filter dependencies.
|
|
248
|
+
"""
|
|
249
|
+
params: list[inspect.Parameter] = []
|
|
250
|
+
annotations: dict[str, Any] = {}
|
|
251
|
+
|
|
252
|
+
if (id_type := config.get("id_filter", False)) is not False:
|
|
253
|
+
_add_dependency(
|
|
254
|
+
params,
|
|
255
|
+
annotations,
|
|
256
|
+
dep_defaults.ID_FILTER_DEPENDENCY_KEY,
|
|
257
|
+
_IdFilterProvider(config.get("id_field", "id"), id_type if isinstance(id_type, type) else object),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if config.get("created_at", False):
|
|
261
|
+
_add_dependency(
|
|
262
|
+
params,
|
|
263
|
+
annotations,
|
|
264
|
+
dep_defaults.CREATED_FILTER_DEPENDENCY_KEY,
|
|
265
|
+
_BeforeAfterFilterProvider("created_at", "createdBefore", "createdAfter"),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
if config.get("updated_at", False):
|
|
269
|
+
_add_dependency(
|
|
270
|
+
params,
|
|
271
|
+
annotations,
|
|
272
|
+
dep_defaults.UPDATED_FILTER_DEPENDENCY_KEY,
|
|
273
|
+
_BeforeAfterFilterProvider("updated_at", "updatedBefore", "updatedAfter"),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if config.get("pagination_type") == "limit_offset":
|
|
277
|
+
_add_dependency(
|
|
278
|
+
params,
|
|
279
|
+
annotations,
|
|
280
|
+
dep_defaults.LIMIT_OFFSET_FILTER_DEPENDENCY_KEY,
|
|
281
|
+
_LimitOffsetFilterProvider(config.get("pagination_size", dep_defaults.DEFAULT_PAGINATION_SIZE)),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
if search_fields := config.get("search"):
|
|
285
|
+
_add_dependency(
|
|
286
|
+
params,
|
|
287
|
+
annotations,
|
|
288
|
+
dep_defaults.SEARCH_FILTER_DEPENDENCY_KEY,
|
|
289
|
+
_SearchFilterProvider(search_fields, config.get("search_ignore_case", False)),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if sort_field := config.get("sort_field"):
|
|
293
|
+
_add_dependency(
|
|
294
|
+
params, annotations, dep_defaults.ORDER_BY_FILTER_DEPENDENCY_KEY, _OrderByProvider(sort_field, config)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if not_in_fields := config.get("not_in_fields"):
|
|
298
|
+
not_in_fields = {not_in_fields} if isinstance(not_in_fields, (str, FieldNameType)) else not_in_fields
|
|
299
|
+
for field_def in not_in_fields:
|
|
300
|
+
resolved_field: FieldNameType = (
|
|
301
|
+
FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
param_name = f"{resolved_field.name}_not_in_filter"
|
|
305
|
+
_add_dependency(params, annotations, param_name, _CollectionFilterProvider(resolved_field, negated=True))
|
|
306
|
+
|
|
307
|
+
if in_fields := config.get("in_fields"):
|
|
308
|
+
in_fields = {in_fields} if isinstance(in_fields, (str, FieldNameType)) else in_fields
|
|
309
|
+
for field_def in in_fields:
|
|
310
|
+
resolved_field = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def
|
|
311
|
+
|
|
312
|
+
param_name = f"{resolved_field.name}_in_filter"
|
|
313
|
+
_add_dependency(params, annotations, param_name, _CollectionFilterProvider(resolved_field, negated=False))
|
|
314
|
+
|
|
315
|
+
if null_fields := config.get("null_fields"):
|
|
316
|
+
null_fields = {null_fields} if isinstance(null_fields, str) else null_fields
|
|
317
|
+
for field_name in null_fields:
|
|
318
|
+
param_name = f"{field_name}_null_filter"
|
|
319
|
+
_add_dependency(params, annotations, param_name, _NullFilterProvider(field_name, negated=False))
|
|
320
|
+
|
|
321
|
+
if not_null_fields := config.get("not_null_fields"):
|
|
322
|
+
not_null_fields = {not_null_fields} if isinstance(not_null_fields, str) else not_null_fields
|
|
323
|
+
for field_name in not_null_fields:
|
|
324
|
+
param_name = f"{field_name}_not_null_filter"
|
|
325
|
+
_add_dependency(params, annotations, param_name, _NullFilterProvider(field_name, negated=True))
|
|
326
|
+
|
|
327
|
+
return _make_aggregate_filter_provider(params, annotations)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _empty_filter_list() -> "list[FilterTypes]":
|
|
331
|
+
return []
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _make_hashable(value: Any) -> HashableType:
|
|
335
|
+
"""Convert a value into a hashable type for caching purposes.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
value: Any value that needs to be made hashable.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
A hashable version of the value.
|
|
342
|
+
"""
|
|
343
|
+
if isinstance(value, dict):
|
|
344
|
+
items = []
|
|
345
|
+
for k in sorted(value.keys()):
|
|
346
|
+
v = value[k]
|
|
347
|
+
items.append((str(k), _make_hashable(v)))
|
|
348
|
+
return tuple(items)
|
|
349
|
+
if isinstance(value, (list, set)):
|
|
350
|
+
hashable_items = [_make_hashable(item) for item in value]
|
|
351
|
+
filtered_items = [item for item in hashable_items if item is not None]
|
|
352
|
+
return tuple(sorted(filtered_items, key=str))
|
|
353
|
+
if isinstance(value, (str, int, float, bool, type(None))):
|
|
354
|
+
return value
|
|
355
|
+
return str(value)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _has_filter_config(config: FilterConfig) -> bool:
|
|
359
|
+
for key in _FILTER_CONFIG_KEYS:
|
|
360
|
+
value = config.get(key)
|
|
361
|
+
if value is not None and value is not False and value != []:
|
|
362
|
+
return True
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _collection_value_annotation(collection_type: type[Any], value_type: type[Any]) -> Any:
|
|
367
|
+
return GenericAlias(collection_type, (value_type,)) | None
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _query_parameter_annotation(value_annotation: Any, query: Any) -> Any:
|
|
371
|
+
return Annotated[value_annotation, query]
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _set_provider_metadata(
|
|
375
|
+
provider: Any, signature: inspect.Signature, annotations: dict[str, Any]
|
|
376
|
+
) -> Callable[..., Any]:
|
|
377
|
+
provider.__signature__ = signature
|
|
378
|
+
provider.__annotations__ = annotations
|
|
379
|
+
return cast("Callable[..., Any]", provider)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _aggregate_filter_provider(**kwargs: Any) -> list[FilterTypes]:
|
|
383
|
+
filters: list[FilterTypes] = []
|
|
384
|
+
for filter_value in kwargs.values():
|
|
385
|
+
if filter_value is None:
|
|
386
|
+
continue
|
|
387
|
+
if isinstance(filter_value, list):
|
|
388
|
+
filters.extend(filter_value)
|
|
389
|
+
elif (isinstance(filter_value, SearchFilter) and filter_value.value is None) or (
|
|
390
|
+
isinstance(filter_value, OrderByFilter) and filter_value.field_name is None
|
|
391
|
+
):
|
|
392
|
+
continue
|
|
393
|
+
else:
|
|
394
|
+
filters.append(filter_value)
|
|
395
|
+
return filters
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _make_aggregate_filter_provider(
|
|
399
|
+
parameters: list[inspect.Parameter], annotations: dict[str, Any]
|
|
400
|
+
) -> Callable[..., list[FilterTypes]]:
|
|
401
|
+
aggregate_annotations = dict(annotations)
|
|
402
|
+
aggregate_annotations["return"] = list[FilterTypes]
|
|
403
|
+
return _set_provider_metadata(
|
|
404
|
+
partial(_aggregate_filter_provider),
|
|
405
|
+
inspect.Signature(parameters=parameters, return_annotation=list[FilterTypes]),
|
|
406
|
+
aggregate_annotations,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class _IdFilterProvider:
|
|
411
|
+
def __init__(self, field_name: str, id_type: type[Any]) -> None:
|
|
412
|
+
self.field_name = field_name
|
|
413
|
+
self.return_annotation = InCollectionFilter[id_type] | None # type: ignore[valid-type]
|
|
414
|
+
ids_parameter_annotation = _query_parameter_annotation(
|
|
415
|
+
_collection_value_annotation(list, id_type), Query(alias="ids", description="IDs to filter by.")
|
|
416
|
+
)
|
|
417
|
+
self.__signature__ = inspect.Signature(
|
|
418
|
+
parameters=[
|
|
419
|
+
inspect.Parameter(
|
|
420
|
+
"ids", kind=inspect.Parameter.KEYWORD_ONLY, default=None, annotation=ids_parameter_annotation
|
|
421
|
+
)
|
|
422
|
+
],
|
|
423
|
+
return_annotation=self.return_annotation,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def __call__(self, ids: list[Any] | None = None) -> InCollectionFilter[Any] | None:
|
|
427
|
+
return InCollectionFilter(field_name=self.field_name, values=ids) if ids else None
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class _BeforeAfterFilterProvider:
|
|
431
|
+
def __init__(self, field_name: str, before_alias: str, after_alias: str) -> None:
|
|
432
|
+
self.field_name = field_name
|
|
433
|
+
self.before_alias = before_alias
|
|
434
|
+
self.after_alias = after_alias
|
|
435
|
+
self.before_param = f"{field_name}_before"
|
|
436
|
+
self.after_param = f"{field_name}_after"
|
|
437
|
+
self.return_annotation = BeforeAfterFilter | None
|
|
438
|
+
self.__signature__ = inspect.Signature(
|
|
439
|
+
parameters=[
|
|
440
|
+
inspect.Parameter(
|
|
441
|
+
self.before_param,
|
|
442
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
443
|
+
default=None,
|
|
444
|
+
annotation=Annotated[
|
|
445
|
+
str | None,
|
|
446
|
+
Query(
|
|
447
|
+
alias=before_alias,
|
|
448
|
+
description=f"Filter by {field_name} before this timestamp.",
|
|
449
|
+
json_schema_extra={"format": "date-time"},
|
|
450
|
+
),
|
|
451
|
+
],
|
|
452
|
+
),
|
|
453
|
+
inspect.Parameter(
|
|
454
|
+
self.after_param,
|
|
455
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
456
|
+
default=None,
|
|
457
|
+
annotation=Annotated[
|
|
458
|
+
str | None,
|
|
459
|
+
Query(
|
|
460
|
+
alias=after_alias,
|
|
461
|
+
description=f"Filter by {field_name} after this timestamp.",
|
|
462
|
+
json_schema_extra={"format": "date-time"},
|
|
463
|
+
),
|
|
464
|
+
],
|
|
465
|
+
),
|
|
466
|
+
],
|
|
467
|
+
return_annotation=self.return_annotation,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def __call__(self, **kwargs: Any) -> BeforeAfterFilter | None:
|
|
471
|
+
before_dt = self._parse_datetime(kwargs.get(self.before_param), self.before_alias)
|
|
472
|
+
after_dt = self._parse_datetime(kwargs.get(self.after_param), self.after_alias)
|
|
473
|
+
if before_dt or after_dt:
|
|
474
|
+
return BeforeAfterFilter(field_name=self.field_name, before=before_dt, after=after_dt)
|
|
475
|
+
return None
|
|
476
|
+
|
|
477
|
+
@staticmethod
|
|
478
|
+
def _parse_datetime(value: Any, alias: str) -> datetime.datetime | None:
|
|
479
|
+
if value is None:
|
|
480
|
+
return None
|
|
481
|
+
if isinstance(value, datetime.datetime):
|
|
482
|
+
return value
|
|
483
|
+
try:
|
|
484
|
+
return datetime.datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
485
|
+
except (ValueError, TypeError, AttributeError):
|
|
486
|
+
msg = f"Invalid date format for {alias}"
|
|
487
|
+
raise RequestValidationError(errors=[{"loc": ("query", alias), "msg": msg, "type": "value_error.datetime"}])
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class _LimitOffsetFilterProvider:
|
|
491
|
+
def __init__(self, default_page_size: int) -> None:
|
|
492
|
+
self.default_page_size = default_page_size
|
|
493
|
+
self.return_annotation = LimitOffsetFilter
|
|
494
|
+
self.__signature__ = inspect.Signature(
|
|
495
|
+
parameters=[
|
|
496
|
+
inspect.Parameter(
|
|
497
|
+
"current_page",
|
|
498
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
499
|
+
default=1,
|
|
500
|
+
annotation=Annotated[
|
|
501
|
+
int, Query(ge=1, alias="currentPage", description="Page number for pagination.")
|
|
502
|
+
],
|
|
503
|
+
),
|
|
504
|
+
inspect.Parameter(
|
|
505
|
+
"page_size",
|
|
506
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
507
|
+
default=default_page_size,
|
|
508
|
+
annotation=Annotated[int, Query(ge=1, alias="pageSize", description="Number of items per page.")],
|
|
509
|
+
),
|
|
510
|
+
],
|
|
511
|
+
return_annotation=self.return_annotation,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
def __call__(self, current_page: int = 1, page_size: int | None = None) -> LimitOffsetFilter:
|
|
515
|
+
resolved_page_size = page_size if page_size is not None else self.default_page_size
|
|
516
|
+
return LimitOffsetFilter(limit=resolved_page_size, offset=resolved_page_size * (current_page - 1))
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
class _SearchFilterProvider:
|
|
520
|
+
def __init__(self, search_fields: str | set[str], ignore_case_default: bool) -> None:
|
|
521
|
+
self.search_fields = search_fields
|
|
522
|
+
self.ignore_case_default = ignore_case_default
|
|
523
|
+
self.return_annotation = SearchFilter | None
|
|
524
|
+
self.__signature__ = inspect.Signature(
|
|
525
|
+
parameters=[
|
|
526
|
+
inspect.Parameter(
|
|
527
|
+
"search_string",
|
|
528
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
529
|
+
default=None,
|
|
530
|
+
annotation=Annotated[str | None, Query(alias="searchString", description="Search term.")],
|
|
531
|
+
),
|
|
532
|
+
inspect.Parameter(
|
|
533
|
+
"ignore_case",
|
|
534
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
535
|
+
default=ignore_case_default,
|
|
536
|
+
annotation=Annotated[
|
|
537
|
+
bool | None,
|
|
538
|
+
Query(alias="searchIgnoreCase", description="Whether search should be case-insensitive."),
|
|
539
|
+
],
|
|
540
|
+
),
|
|
541
|
+
],
|
|
542
|
+
return_annotation=self.return_annotation,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def __call__(self, search_string: str | None = None, ignore_case: bool | None = None) -> SearchFilter | None:
|
|
546
|
+
field_names: set[Any] = (
|
|
547
|
+
set(self.search_fields.split(",")) if isinstance(self.search_fields, str) else set(self.search_fields)
|
|
548
|
+
)
|
|
549
|
+
if search_string:
|
|
550
|
+
return SearchFilter(
|
|
551
|
+
field_name=field_names,
|
|
552
|
+
value=search_string,
|
|
553
|
+
ignore_case=self.ignore_case_default if ignore_case is None else ignore_case,
|
|
554
|
+
)
|
|
555
|
+
return None
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
class _OrderByProvider:
|
|
559
|
+
def __init__(self, sort_field: SortField, config: FilterConfig) -> None:
|
|
560
|
+
self.sort_resolution = _resolve_sort_field_aliases(
|
|
561
|
+
sort_field,
|
|
562
|
+
sort_field_aliases=config.get("sort_field_aliases"),
|
|
563
|
+
sort_field_camelize=config.get("sort_field_camelize", True),
|
|
564
|
+
)
|
|
565
|
+
self.sort_order_default: SortOrder = config.get("sort_order", "desc")
|
|
566
|
+
self.allowed_field_names = ", ".join(self.sort_resolution.allowed_display_names)
|
|
567
|
+
self.return_annotation = OrderByFilter
|
|
568
|
+
self.__signature__ = inspect.Signature(
|
|
569
|
+
parameters=[
|
|
570
|
+
inspect.Parameter(
|
|
571
|
+
"field_name",
|
|
572
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
573
|
+
default=self.sort_resolution.default_query_value,
|
|
574
|
+
annotation=Annotated[str, Query(alias="orderBy", description="Field to order by.")],
|
|
575
|
+
),
|
|
576
|
+
inspect.Parameter(
|
|
577
|
+
"sort_order",
|
|
578
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
579
|
+
default=self.sort_order_default,
|
|
580
|
+
annotation=Annotated[
|
|
581
|
+
SortOrder | None, Query(alias="sortOrder", description="Sort order ('asc' or 'desc').")
|
|
582
|
+
],
|
|
583
|
+
),
|
|
584
|
+
],
|
|
585
|
+
return_annotation=self.return_annotation,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
def __call__(self, field_name: str | None = None, sort_order: SortOrder | None = None) -> OrderByFilter:
|
|
589
|
+
query_value = field_name or self.sort_resolution.default_query_value
|
|
590
|
+
resolved_field = self.sort_resolution.normalize(query_value)
|
|
591
|
+
if resolved_field is None:
|
|
592
|
+
msg = f"Invalid orderBy field '{query_value}'. Allowed fields: {self.allowed_field_names}"
|
|
593
|
+
raise RequestValidationError(errors=[{"loc": ("query", "orderBy"), "msg": msg, "type": "value_error"}])
|
|
594
|
+
return OrderByFilter(field_name=resolved_field, sort_order=sort_order or self.sort_order_default)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _resolve_sort_field_aliases(
|
|
598
|
+
sort_field: SortField, sort_field_aliases: Mapping[str, str] | None = None, sort_field_camelize: bool = True
|
|
599
|
+
) -> _SortFieldResolution:
|
|
600
|
+
fields = _coerce_sort_fields(sort_field)
|
|
601
|
+
allowed_fields = frozenset(fields)
|
|
602
|
+
inbound_aliases: dict[str, str] = {}
|
|
603
|
+
field_display_names = {field: field for field in fields}
|
|
604
|
+
|
|
605
|
+
for field in fields:
|
|
606
|
+
_add_sort_field_alias(inbound_aliases, alias=field, field=field)
|
|
607
|
+
|
|
608
|
+
if sort_field_camelize:
|
|
609
|
+
for field in fields:
|
|
610
|
+
alias = camelize(field)
|
|
611
|
+
_add_sort_field_alias(inbound_aliases, alias=alias, field=field)
|
|
612
|
+
field_display_names[field] = alias
|
|
613
|
+
|
|
614
|
+
if sort_field_aliases:
|
|
615
|
+
for alias, field in sort_field_aliases.items():
|
|
616
|
+
if field not in allowed_fields:
|
|
617
|
+
msg = f"sort field alias '{alias}' targets unknown sort field '{field}'"
|
|
618
|
+
raise ValueError(msg)
|
|
619
|
+
_add_sort_field_alias(inbound_aliases, alias=alias, field=field)
|
|
620
|
+
field_display_names[field] = alias
|
|
621
|
+
|
|
622
|
+
allowed_display_names = tuple(field_display_names[field] for field in fields)
|
|
623
|
+
return _SortFieldResolution(
|
|
624
|
+
default_field=fields[0],
|
|
625
|
+
default_query_value=field_display_names[fields[0]],
|
|
626
|
+
allowed_fields=allowed_fields,
|
|
627
|
+
inbound_aliases=inbound_aliases,
|
|
628
|
+
field_display_names=field_display_names,
|
|
629
|
+
allowed_display_names=allowed_display_names,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _coerce_sort_fields(sort_field: SortField) -> tuple[str, ...]:
|
|
634
|
+
if isinstance(sort_field, str):
|
|
635
|
+
return (sort_field,)
|
|
636
|
+
fields = tuple(sorted(sort_field)) if isinstance(sort_field, set) else tuple(sort_field)
|
|
637
|
+
if not fields:
|
|
638
|
+
msg = "sort_field must include at least one field"
|
|
639
|
+
raise ValueError(msg)
|
|
640
|
+
return fields
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _add_sort_field_alias(inbound_aliases: dict[str, str], *, alias: str, field: str) -> None:
|
|
644
|
+
existing_field = inbound_aliases.get(alias)
|
|
645
|
+
if existing_field is None or existing_field == field:
|
|
646
|
+
inbound_aliases[alias] = field
|
|
647
|
+
return
|
|
648
|
+
|
|
649
|
+
msg = f"ambiguous sort field alias '{alias}' maps to both '{existing_field}' and '{field}'"
|
|
650
|
+
raise ValueError(msg)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
class _CollectionFilterProvider:
|
|
654
|
+
def __init__(self, field: FieldNameType, *, negated: bool) -> None:
|
|
655
|
+
self.field_name = field.name
|
|
656
|
+
self.type_hint = field.type_hint
|
|
657
|
+
self.param_name = f"{field.name}_{'not_in' if negated else 'in'}_values"
|
|
658
|
+
self.filter_cls: Any = NotInCollectionFilter if negated else InCollectionFilter
|
|
659
|
+
query_suffix = "not_in" if negated else "in"
|
|
660
|
+
parameter_annotation = _query_parameter_annotation(
|
|
661
|
+
_collection_value_annotation(set, field.type_hint),
|
|
662
|
+
Query(
|
|
663
|
+
alias=camelize(f"{field.name}_{query_suffix}"), description=f"Filter {field.name} {query_suffix} values"
|
|
664
|
+
),
|
|
665
|
+
)
|
|
666
|
+
self.return_annotation = self.filter_cls[field.type_hint] | None
|
|
667
|
+
self.__signature__ = inspect.Signature(
|
|
668
|
+
parameters=[
|
|
669
|
+
inspect.Parameter(
|
|
670
|
+
self.param_name, kind=inspect.Parameter.KEYWORD_ONLY, default=None, annotation=parameter_annotation
|
|
671
|
+
)
|
|
672
|
+
],
|
|
673
|
+
return_annotation=self.return_annotation,
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
def __call__(self, **kwargs: Any) -> Any:
|
|
677
|
+
values = kwargs.get(self.param_name)
|
|
678
|
+
return self.filter_cls[self.type_hint](field_name=self.field_name, values=values) if values else None
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
class _NullFilterProvider:
|
|
682
|
+
def __init__(self, field_name: str, *, negated: bool) -> None:
|
|
683
|
+
self.field_name = field_name
|
|
684
|
+
self.param_name = f"{field_name}_{'is_not_null' if negated else 'is_null'}"
|
|
685
|
+
self.filter_cls: type[Any] = NotNullFilter if negated else NullFilter
|
|
686
|
+
self.return_annotation = self.filter_cls | None
|
|
687
|
+
self.__signature__ = inspect.Signature(
|
|
688
|
+
parameters=[
|
|
689
|
+
inspect.Parameter(
|
|
690
|
+
self.param_name,
|
|
691
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
692
|
+
default=None,
|
|
693
|
+
annotation=Annotated[
|
|
694
|
+
bool | None,
|
|
695
|
+
Query(
|
|
696
|
+
alias=camelize(self.param_name),
|
|
697
|
+
description=f"Filter where {field_name} {'IS NOT NULL' if negated else 'IS NULL'}",
|
|
698
|
+
),
|
|
699
|
+
],
|
|
700
|
+
)
|
|
701
|
+
],
|
|
702
|
+
return_annotation=self.return_annotation,
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
def __call__(self, **kwargs: Any) -> Any:
|
|
706
|
+
return self.filter_cls(field_name=self.field_name) if kwargs.get(self.param_name) else None
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _add_dependency(params: list[inspect.Parameter], annotations: dict[str, Any], name: str, provider: Any) -> None:
|
|
710
|
+
dependency_annotation = _query_parameter_annotation(provider.return_annotation, Depends(provider))
|
|
711
|
+
params.append(inspect.Parameter(name=name, kind=inspect.Parameter.KEYWORD_ONLY, annotation=dependency_annotation))
|
|
712
|
+
annotations[name] = dependency_annotation
|