kiba-core 0.5.3.dev90__tar.gz → 0.5.3.dev92__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.
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/Dockerfile +1 -1
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/PKG-INFO +10 -10
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/store/entity_repository.py +12 -2
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/kiba_core.egg-info/PKG-INFO +10 -10
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/kiba_core.egg-info/requires.txt +9 -9
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/pyproject.toml +11 -11
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/test_entity_repository.py +43 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/uv.lock +354 -354
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.github/pull_request_template.md +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.github/workflows/deploy.yml +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.github/workflows/publish.yml +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.github/workflows/pull-request.yml +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.github/workflows/release.yml +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/.gitignore +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/CHANGELOG.md +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/README.md +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/api_request.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/api_response.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/authorizer.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/default_routes.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/health.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/json_route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/logging_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/middleware/server_headers_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/openapi.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/rate_limit.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/request_body.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/request_context.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/route_auth.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/route_metadata.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/api/uploaded_file.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/aws_requester.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/caching/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/caching/cache.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/caching/dict_cache.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/caching/file_cache.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/cron/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/cron/api_request_cron_job_processor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/cron/cron_executor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/cron/cron_job_processor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/cron/model.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/exceptions.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/http/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/http/basic_authentication.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/http/jwt.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/http/rest_method.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/locks/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/locks/lock.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/locks/model.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/locks/sql.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/logging.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/notifications/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/notifications/discord_client.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/notifications/notification_client.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/notifications/slack_client.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/py.typed +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/aqs.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/cosmos.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/message_queue.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/message_queue_processor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/model.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/sql.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/queues/sqs.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/requester/requester.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/s3_manager.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/service_client.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/store/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/store/database.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/store/retriever.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/store/saver.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/async_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/chain_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/date_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/dict_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/file_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/hashing_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/http_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/json_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/list_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/process_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/string_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/typing_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/url_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/util/value_holder.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/web3/eth_client.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/web3/eth_client_registry.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/core/web3/multicall3.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/kiba_core.egg-info/SOURCES.txt +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/kiba_core.egg-info/dependency_links.txt +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/kiba_core.egg-info/top_level.txt +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/makefile +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/setup.cfg +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_api_response.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_authorizer.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_json_route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_openapi.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_rate_limit.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_request_context.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/api/test_streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/caching/test_dict_cache.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/caching/test_file_cache.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/cron/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/cron/test_cron_executor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/locks/test_sql_lock.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/conftest.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/test_aqs_queue.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/test_cosmos_queue.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/test_message_queue_processor.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/test_sql_queue.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/queues/test_sqs_queue.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/requester/test_requester.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/test_database.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/test_exceptions.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/test_logging.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_async_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_chain_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_date_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_dict_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_file_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_json_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_list_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_process_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_string_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/util/test_url_util.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/web3/test_eth_client_registry.py +0 -0
- {kiba_core-0.5.3.dev90 → kiba_core-0.5.3.dev92}/tests/web3/test_rest_eth_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kiba-core
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev92
|
|
4
4
|
Summary: Kiba Labs' python utilities
|
|
5
5
|
Project-URL: repository, https://github.com/kibalabs/core-py
|
|
6
6
|
Requires-Python: <4,>=3.12
|
|
@@ -9,36 +9,36 @@ Requires-Dist: aiofiles>=25.1.0
|
|
|
9
9
|
Requires-Dist: pydantic>=2.14.1
|
|
10
10
|
Requires-Dist: orjson>=3.13.0
|
|
11
11
|
Provides-Extra: api
|
|
12
|
-
Requires-Dist: fastapi>=0.
|
|
12
|
+
Requires-Dist: fastapi>=0.162.0; extra == "api"
|
|
13
13
|
Requires-Dist: uvicorn[standard]>=0.54.0; extra == "api"
|
|
14
14
|
Provides-Extra: core-api
|
|
15
|
-
Requires-Dist: starlette>=1.
|
|
15
|
+
Requires-Dist: starlette>=1.10.0; extra == "core-api"
|
|
16
16
|
Requires-Dist: uvicorn[standard]>=0.54.0; extra == "core-api"
|
|
17
17
|
Requires-Dist: python-multipart>=0.0.32; extra == "core-api"
|
|
18
18
|
Provides-Extra: storage
|
|
19
|
-
Requires-Dist: aiobotocore>=3.
|
|
19
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "storage"
|
|
20
20
|
Provides-Extra: queues
|
|
21
|
-
Requires-Dist: aiobotocore>=3.
|
|
21
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "queues"
|
|
22
22
|
Provides-Extra: queue-sqs
|
|
23
|
-
Requires-Dist: aiobotocore>=3.
|
|
23
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "queue-sqs"
|
|
24
24
|
Provides-Extra: queue-aqs
|
|
25
25
|
Requires-Dist: azure-storage-queue[aio]>=12.18.0; extra == "queue-aqs"
|
|
26
26
|
Provides-Extra: queue-cosmos
|
|
27
27
|
Requires-Dist: azure-cosmos>=4.17.1; extra == "queue-cosmos"
|
|
28
28
|
Provides-Extra: database-psql
|
|
29
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.1.
|
|
29
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.1.6; extra == "database-psql"
|
|
30
30
|
Requires-Dist: asyncpg>=0.32.0; extra == "database-psql"
|
|
31
31
|
Provides-Extra: database-sqlite
|
|
32
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.1.
|
|
32
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.1.6; extra == "database-sqlite"
|
|
33
33
|
Requires-Dist: aiosqlite>=0.22.1; extra == "database-sqlite"
|
|
34
34
|
Provides-Extra: requester
|
|
35
|
-
Requires-Dist: httpx2>=2.
|
|
35
|
+
Requires-Dist: httpx2>=2.16.0; extra == "requester"
|
|
36
36
|
Provides-Extra: web3
|
|
37
37
|
Requires-Dist: web3<8.0.0,>=7.16.0; extra == "web3"
|
|
38
38
|
Provides-Extra: cron
|
|
39
39
|
Requires-Dist: apscheduler<4,>=3.11.3; extra == "cron"
|
|
40
40
|
Provides-Extra: types
|
|
41
|
-
Requires-Dist: types-aiobotocore[essential]>=3.
|
|
41
|
+
Requires-Dist: types-aiobotocore[essential]>=3.10.0; extra == "types"
|
|
42
42
|
Requires-Dist: types-aiofiles>=25.1.0.20260518; extra == "types"
|
|
43
43
|
|
|
44
44
|
# Core-Py
|
|
@@ -9,6 +9,7 @@ from sqlalchemy.dialects import sqlite as sqlalchemy_sqlite
|
|
|
9
9
|
from sqlalchemy.engine import Result
|
|
10
10
|
from sqlalchemy.engine import RowMapping
|
|
11
11
|
from sqlalchemy.sql import Select
|
|
12
|
+
from sqlalchemy.sql.base import ReadOnlyColumnCollection
|
|
12
13
|
|
|
13
14
|
from core.exceptions import KibaException
|
|
14
15
|
from core.exceptions import NotFoundException
|
|
@@ -25,6 +26,7 @@ from core.store.retriever import uuid_from_value
|
|
|
25
26
|
from core.util import date_util
|
|
26
27
|
|
|
27
28
|
EntityType = typing.TypeVar('EntityType', bound=BaseModel)
|
|
29
|
+
UpsertUpdateWhere = typing.Callable[[ReadOnlyColumnCollection[str, typing.Any]], sqlalchemy.ColumnElement[bool]] # type: ignore[explicit-any]
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
class EntityRepository(typing.Generic[EntityType]): # noqa: UP046
|
|
@@ -135,7 +137,8 @@ class EntityRepository(typing.Generic[EntityType]): # noqa: UP046
|
|
|
135
137
|
result = await database.execute(query=self.table.update().where(self.idColumn == idValue).values(updateValues).returning(*self.list_select_columns()), connection=connection)
|
|
136
138
|
return self.force_from_result(result=result)
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
# NOTE(krishan711): updateWhere gets the conflicting row's new values (`excluded`) and only updates when it is true; when it is false nothing is written and the stored row is returned
|
|
141
|
+
async def upsert(self, database: Database, constraintColumnNames: list[str], connection: DatabaseConnection | None = None, updateWhere: UpsertUpdateWhere | None = None, **kwargs) -> EntityType: # type: ignore[no-untyped-def] # noqa: ANN003
|
|
139
142
|
updateValues = self._create_values(kwargs=kwargs, shouldAddUpdatedDate=True, shouldAddCreatedDate=False)
|
|
140
143
|
insertValues = self._create_values(kwargs=kwargs, shouldAddUpdatedDate=True, shouldAddCreatedDate=True)
|
|
141
144
|
self._add_generated_id(values=insertValues)
|
|
@@ -144,9 +147,16 @@ class EntityRepository(typing.Generic[EntityType]): # noqa: UP046
|
|
|
144
147
|
doUpdateStatement = insertStatement.on_conflict_do_update(
|
|
145
148
|
index_elements=constraintColumns,
|
|
146
149
|
set_=updateValues,
|
|
150
|
+
where=updateWhere(insertStatement.excluded) if updateWhere is not None else None,
|
|
147
151
|
)
|
|
148
152
|
result = await database.execute(query=doUpdateStatement.returning(*self.list_select_columns()), connection=connection)
|
|
149
|
-
|
|
153
|
+
row = result.mappings().first()
|
|
154
|
+
if row is None and updateWhere is not None:
|
|
155
|
+
storedQuery = self.build_select().where(*[column == insertValues[column] for column in constraintColumns])
|
|
156
|
+
row = (await database.execute(query=storedQuery, connection=connection)).mappings().first()
|
|
157
|
+
if row is None:
|
|
158
|
+
raise NotFoundException
|
|
159
|
+
return self.from_row(row=row)
|
|
150
160
|
|
|
151
161
|
async def upsert_many(self, database: Database, constraintColumnNames: list[str], rowDicts: list[dict[str, typing.Any]], connection: DatabaseConnection | None = None) -> list[EntityType]: # type: ignore[explicit-any]
|
|
152
162
|
if not rowDicts:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kiba-core
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev92
|
|
4
4
|
Summary: Kiba Labs' python utilities
|
|
5
5
|
Project-URL: repository, https://github.com/kibalabs/core-py
|
|
6
6
|
Requires-Python: <4,>=3.12
|
|
@@ -9,36 +9,36 @@ Requires-Dist: aiofiles>=25.1.0
|
|
|
9
9
|
Requires-Dist: pydantic>=2.14.1
|
|
10
10
|
Requires-Dist: orjson>=3.13.0
|
|
11
11
|
Provides-Extra: api
|
|
12
|
-
Requires-Dist: fastapi>=0.
|
|
12
|
+
Requires-Dist: fastapi>=0.162.0; extra == "api"
|
|
13
13
|
Requires-Dist: uvicorn[standard]>=0.54.0; extra == "api"
|
|
14
14
|
Provides-Extra: core-api
|
|
15
|
-
Requires-Dist: starlette>=1.
|
|
15
|
+
Requires-Dist: starlette>=1.10.0; extra == "core-api"
|
|
16
16
|
Requires-Dist: uvicorn[standard]>=0.54.0; extra == "core-api"
|
|
17
17
|
Requires-Dist: python-multipart>=0.0.32; extra == "core-api"
|
|
18
18
|
Provides-Extra: storage
|
|
19
|
-
Requires-Dist: aiobotocore>=3.
|
|
19
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "storage"
|
|
20
20
|
Provides-Extra: queues
|
|
21
|
-
Requires-Dist: aiobotocore>=3.
|
|
21
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "queues"
|
|
22
22
|
Provides-Extra: queue-sqs
|
|
23
|
-
Requires-Dist: aiobotocore>=3.
|
|
23
|
+
Requires-Dist: aiobotocore>=3.10.0; extra == "queue-sqs"
|
|
24
24
|
Provides-Extra: queue-aqs
|
|
25
25
|
Requires-Dist: azure-storage-queue[aio]>=12.18.0; extra == "queue-aqs"
|
|
26
26
|
Provides-Extra: queue-cosmos
|
|
27
27
|
Requires-Dist: azure-cosmos>=4.17.1; extra == "queue-cosmos"
|
|
28
28
|
Provides-Extra: database-psql
|
|
29
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.1.
|
|
29
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.1.6; extra == "database-psql"
|
|
30
30
|
Requires-Dist: asyncpg>=0.32.0; extra == "database-psql"
|
|
31
31
|
Provides-Extra: database-sqlite
|
|
32
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.1.
|
|
32
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.1.6; extra == "database-sqlite"
|
|
33
33
|
Requires-Dist: aiosqlite>=0.22.1; extra == "database-sqlite"
|
|
34
34
|
Provides-Extra: requester
|
|
35
|
-
Requires-Dist: httpx2>=2.
|
|
35
|
+
Requires-Dist: httpx2>=2.16.0; extra == "requester"
|
|
36
36
|
Provides-Extra: web3
|
|
37
37
|
Requires-Dist: web3<8.0.0,>=7.16.0; extra == "web3"
|
|
38
38
|
Provides-Extra: cron
|
|
39
39
|
Requires-Dist: apscheduler<4,>=3.11.3; extra == "cron"
|
|
40
40
|
Provides-Extra: types
|
|
41
|
-
Requires-Dist: types-aiobotocore[essential]>=3.
|
|
41
|
+
Requires-Dist: types-aiobotocore[essential]>=3.10.0; extra == "types"
|
|
42
42
|
Requires-Dist: types-aiofiles>=25.1.0.20260518; extra == "types"
|
|
43
43
|
|
|
44
44
|
# Core-Py
|
|
@@ -3,11 +3,11 @@ pydantic>=2.14.1
|
|
|
3
3
|
orjson>=3.13.0
|
|
4
4
|
|
|
5
5
|
[api]
|
|
6
|
-
fastapi>=0.
|
|
6
|
+
fastapi>=0.162.0
|
|
7
7
|
uvicorn[standard]>=0.54.0
|
|
8
8
|
|
|
9
9
|
[core-api]
|
|
10
|
-
starlette>=1.
|
|
10
|
+
starlette>=1.10.0
|
|
11
11
|
uvicorn[standard]>=0.54.0
|
|
12
12
|
python-multipart>=0.0.32
|
|
13
13
|
|
|
@@ -15,11 +15,11 @@ python-multipart>=0.0.32
|
|
|
15
15
|
apscheduler<4,>=3.11.3
|
|
16
16
|
|
|
17
17
|
[database-psql]
|
|
18
|
-
sqlalchemy[asyncio]>=2.1.
|
|
18
|
+
sqlalchemy[asyncio]>=2.1.6
|
|
19
19
|
asyncpg>=0.32.0
|
|
20
20
|
|
|
21
21
|
[database-sqlite]
|
|
22
|
-
sqlalchemy[asyncio]>=2.1.
|
|
22
|
+
sqlalchemy[asyncio]>=2.1.6
|
|
23
23
|
aiosqlite>=0.22.1
|
|
24
24
|
|
|
25
25
|
[queue-aqs]
|
|
@@ -29,19 +29,19 @@ azure-storage-queue[aio]>=12.18.0
|
|
|
29
29
|
azure-cosmos>=4.17.1
|
|
30
30
|
|
|
31
31
|
[queue-sqs]
|
|
32
|
-
aiobotocore>=3.
|
|
32
|
+
aiobotocore>=3.10.0
|
|
33
33
|
|
|
34
34
|
[queues]
|
|
35
|
-
aiobotocore>=3.
|
|
35
|
+
aiobotocore>=3.10.0
|
|
36
36
|
|
|
37
37
|
[requester]
|
|
38
|
-
httpx2>=2.
|
|
38
|
+
httpx2>=2.16.0
|
|
39
39
|
|
|
40
40
|
[storage]
|
|
41
|
-
aiobotocore>=3.
|
|
41
|
+
aiobotocore>=3.10.0
|
|
42
42
|
|
|
43
43
|
[types]
|
|
44
|
-
types-aiobotocore[essential]>=3.
|
|
44
|
+
types-aiobotocore[essential]>=3.10.0
|
|
45
45
|
types-aiofiles>=25.1.0.20260518
|
|
46
46
|
|
|
47
47
|
[web3]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kiba-core"
|
|
3
|
-
version = "0.5.3.
|
|
3
|
+
version = "0.5.3.dev92"
|
|
4
4
|
description = "Kiba Labs' python utilities"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12, <4"
|
|
@@ -12,23 +12,23 @@ dependencies = [
|
|
|
12
12
|
|
|
13
13
|
[project.optional-dependencies]
|
|
14
14
|
api = [
|
|
15
|
-
"fastapi>=0.
|
|
15
|
+
"fastapi>=0.162.0",
|
|
16
16
|
"uvicorn[standard]>=0.54.0",
|
|
17
17
|
]
|
|
18
18
|
core-api = [
|
|
19
|
-
"starlette>=1.
|
|
19
|
+
"starlette>=1.10.0",
|
|
20
20
|
"uvicorn[standard]>=0.54.0",
|
|
21
21
|
"python-multipart>=0.0.32",
|
|
22
22
|
]
|
|
23
23
|
storage = [
|
|
24
|
-
"aiobotocore>=3.
|
|
24
|
+
"aiobotocore>=3.10.0",
|
|
25
25
|
]
|
|
26
26
|
# TODO(krishan711): remove this in next major update
|
|
27
27
|
queues = [
|
|
28
|
-
"aiobotocore>=3.
|
|
28
|
+
"aiobotocore>=3.10.0",
|
|
29
29
|
]
|
|
30
30
|
queue-sqs = [
|
|
31
|
-
"aiobotocore>=3.
|
|
31
|
+
"aiobotocore>=3.10.0",
|
|
32
32
|
]
|
|
33
33
|
queue-aqs = [
|
|
34
34
|
"azure-storage-queue[aio]>=12.18.0",
|
|
@@ -37,15 +37,15 @@ queue-cosmos = [
|
|
|
37
37
|
"azure-cosmos>=4.17.1",
|
|
38
38
|
]
|
|
39
39
|
database-psql = [
|
|
40
|
-
"sqlalchemy[asyncio]>=2.1.
|
|
40
|
+
"sqlalchemy[asyncio]>=2.1.6",
|
|
41
41
|
"asyncpg>=0.32.0",
|
|
42
42
|
]
|
|
43
43
|
database-sqlite = [
|
|
44
|
-
"sqlalchemy[asyncio]>=2.1.
|
|
44
|
+
"sqlalchemy[asyncio]>=2.1.6",
|
|
45
45
|
"aiosqlite>=0.22.1",
|
|
46
46
|
]
|
|
47
47
|
requester = [
|
|
48
|
-
"httpx2>=2.
|
|
48
|
+
"httpx2>=2.16.0",
|
|
49
49
|
]
|
|
50
50
|
web3 = [
|
|
51
51
|
"web3>=7.16.0,<8.0.0",
|
|
@@ -54,13 +54,13 @@ cron = [
|
|
|
54
54
|
"apscheduler>=3.11.3,<4",
|
|
55
55
|
]
|
|
56
56
|
types = [
|
|
57
|
-
"types-aiobotocore[essential]>=3.
|
|
57
|
+
"types-aiobotocore[essential]>=3.10.0",
|
|
58
58
|
"types-aiofiles>=25.1.0.20260518",
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[dependency-groups]
|
|
62
62
|
dev = [
|
|
63
|
-
"kiba-build==0.1.11.
|
|
63
|
+
"kiba-build==0.1.11.dev22",
|
|
64
64
|
]
|
|
65
65
|
|
|
66
66
|
[project.urls]
|
|
@@ -172,6 +172,49 @@ async def test_upsert_inserts_then_updates(database: Database):
|
|
|
172
172
|
assert upsertedItem.count == 2
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
async def test_upsert_with_false_update_where_keeps_and_returns_the_stored_row(database: Database):
|
|
176
|
+
item = await _create_item(database=database, name='a', count=1)
|
|
177
|
+
async with database.create_transaction() as connection:
|
|
178
|
+
upsertedItem = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=lambda excluded: excluded.count > ItemsTable.c.count, name='a', count=0, amount=5)
|
|
179
|
+
storedItem = await ItemsRepository.get(database=database, connection=connection, idValue=item.itemId)
|
|
180
|
+
assert upsertedItem == item
|
|
181
|
+
assert storedItem == item
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def test_upsert_with_false_update_where_finds_the_stored_row_by_converted_constraint_values(database: Database):
|
|
185
|
+
repository = UpperNameEntityRepository(table=ItemsTable, modelClass=Item)
|
|
186
|
+
async with database.create_transaction() as connection:
|
|
187
|
+
item = await repository.create(database=database, connection=connection, name='a', count=1, amount=1)
|
|
188
|
+
upsertedItem = await repository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=lambda _excluded: sqlalchemy.false(), name='a', count=2, amount=1)
|
|
189
|
+
assert upsertedItem == item
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
async def test_upsert_with_true_update_where_updates_the_row(database: Database):
|
|
193
|
+
item = await _create_item(database=database, name='a', count=1)
|
|
194
|
+
async with database.create_transaction() as connection:
|
|
195
|
+
upsertedItem = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=lambda excluded: excluded.count > ItemsTable.c.count, name='a', count=2, amount=5)
|
|
196
|
+
assert (upsertedItem.itemId, upsertedItem.createdDate, upsertedItem.count, upsertedItem.amount) == (item.itemId, item.createdDate, 2, 5)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async def test_upsert_with_update_where_still_inserts_new_rows(database: Database):
|
|
200
|
+
async with database.create_transaction() as connection:
|
|
201
|
+
upsertedItem = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=lambda _excluded: sqlalchemy.false(), name='a', count=1, amount=1)
|
|
202
|
+
storedItems = await ItemsRepository.list_many(database=database, connection=connection, fieldFilters=[])
|
|
203
|
+
assert storedItems == [upsertedItem]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
async def test_upsert_only_when_values_changed(database: Database):
|
|
207
|
+
def changed(excluded: typing.Any) -> sqlalchemy.ColumnElement[bool]: # type: ignore[explicit-any]
|
|
208
|
+
return sqlalchemy.or_(ItemsTable.c.count.is_distinct_from(excluded.count), ItemsTable.c.expiryDate.is_distinct_from(excluded.expiryDate))
|
|
209
|
+
|
|
210
|
+
async with database.create_transaction() as connection:
|
|
211
|
+
item = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=changed, name='a', count=1, amount=1, expiryDate=None)
|
|
212
|
+
unchangedItem = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=changed, name='a', count=1, amount=1, expiryDate=None)
|
|
213
|
+
changedItem = await ItemsRepository.upsert(database=database, connection=connection, constraintColumnNames=['name'], updateWhere=changed, name='a', count=1, amount=1, expiryDate=datetime.datetime(2030, 1, 1, tzinfo=datetime.UTC))
|
|
214
|
+
assert unchangedItem == item
|
|
215
|
+
assert (changedItem.itemId, changedItem.expiryDate) == (item.itemId, datetime.datetime(2030, 1, 1, tzinfo=datetime.UTC))
|
|
216
|
+
|
|
217
|
+
|
|
175
218
|
async def test_upsert_many_inserts_and_updates(database: Database):
|
|
176
219
|
item = await _create_item(database=database, name='a')
|
|
177
220
|
async with database.create_transaction() as connection:
|