haiway 0.12.1__tar.gz → 0.13.0__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.
- {haiway-0.12.1 → haiway-0.13.0}/LICENSE +2 -2
- {haiway-0.12.1 → haiway-0.13.0}/PKG-INFO +5 -5
- {haiway-0.12.1 → haiway-0.13.0}/README.md +2 -2
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/Dockerfile +27 -1
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/Makefile +6 -2
- haiway-0.13.0/examples/fastAPI/README.md +3 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/config/unit.json +3 -3
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/docker-compose.yml +25 -1
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/pyproject.toml +5 -8
- haiway-0.13.0/examples/fastAPI/src/features/todos/__init__.py +5 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/features/todos/state.py +11 -0
- haiway-0.13.0/examples/fastAPI/src/features/todos/user_tasks.py +19 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/integrations/postgres/__init__.py +2 -2
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/integrations/postgres/client.py +59 -34
- haiway-0.13.0/examples/fastAPI/src/integrations/postgres/state.py +77 -0
- haiway-0.13.0/examples/fastAPI/src/integrations/postgres/types.py +69 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/__init__.py +3 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/__main__.py +26 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/postgres/__init__.py +5 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/postgres/execution.py +80 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/postgres/migration_0.py +17 -0
- haiway-0.13.0/examples/fastAPI/src/migrations/postgres/types.py +9 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/application.py +2 -2
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/middlewares/context.py +2 -2
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/routes/todos.py +9 -3
- haiway-0.13.0/examples/fastAPI/src/solutions/user_tasks/__init__.py +7 -0
- haiway-0.13.0/examples/fastAPI/src/solutions/user_tasks/postgres.py +218 -0
- haiway-0.13.0/examples/fastAPI/src/solutions/user_tasks/state.py +77 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/solutions/user_tasks/types.py +3 -2
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/uv.lock +65 -52
- {haiway-0.12.1 → haiway-0.13.0}/guidelines/functionalities.md +45 -20
- {haiway-0.12.1 → haiway-0.13.0}/guidelines/packages.md +8 -8
- {haiway-0.12.1 → haiway-0.13.0}/junit/test-results.xml +1 -1
- {haiway-0.12.1 → haiway-0.13.0}/pyproject.toml +2 -2
- haiway-0.13.0/src/haiway/helpers/caching.py +455 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_cache.py +0 -10
- {haiway-0.12.1 → haiway-0.13.0}/uv.lock +63 -63
- haiway-0.12.1/examples/fastAPI/README.md +0 -1
- haiway-0.12.1/examples/fastAPI/src/features/todos/__init__.py +0 -5
- haiway-0.12.1/examples/fastAPI/src/features/todos/calls.py +0 -16
- haiway-0.12.1/examples/fastAPI/src/features/todos/user_tasks.py +0 -17
- haiway-0.12.1/examples/fastAPI/src/integrations/postgres/state.py +0 -64
- haiway-0.12.1/examples/fastAPI/src/integrations/postgres/types.py +0 -27
- haiway-0.12.1/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -12
- haiway-0.12.1/examples/fastAPI/src/solutions/user_tasks/calls.py +0 -54
- haiway-0.12.1/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -106
- haiway-0.12.1/examples/fastAPI/src/solutions/user_tasks/state.py +0 -25
- haiway-0.12.1/src/haiway/helpers/caching.py +0 -356
- {haiway-0.12.1 → haiway-0.13.0}/.github/workflows/ci.yml +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/.github/workflows/publish.yml +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/.gitignore +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/Makefile +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/config/pre-push +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/.dockerignore +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/config/.env.example +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/features/__int__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/features/todos/config.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/features/todos/types.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/integrations/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/integrations/postgres/config.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/__main__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/config.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/middlewares/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/routes/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/server/routes/technical.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/solutions/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/examples/fastAPI/src/solutions/user_tasks/config.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/access.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/disposables.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/identifier.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/logging.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/metrics.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/state.py +5 -5
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/tasks.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/context/types.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/asynchrony.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/metrics.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/throttling.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/timeouted.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/py.typed +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/attributes.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/path.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/requirement.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/structure.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/state/validation.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/types/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/types/default.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/types/frozen.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/types/missing.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/always.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/collections.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/env.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/freezing.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/logs.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/noop.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/src/haiway/utils/queue.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/__init__.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_async_queue.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_attribute_path.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_auto_retry.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_context.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_state.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_streaming.py +0 -0
- {haiway-0.12.1 → haiway-0.13.0}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
MIT License
|
2
2
|
|
3
|
-
Copyright (c) 2024 Miquido
|
3
|
+
Copyright (c) 2024-2025 Miquido
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
SOFTWARE.
|
21
|
+
SOFTWARE.
|
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.0
|
4
4
|
Summary: Framework for dependency injection and state management within structured concurrency model.
|
5
5
|
Project-URL: Homepage, https://miquido.com
|
6
6
|
Project-URL: Repository, https://github.com/miquido/haiway.git
|
7
7
|
Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
|
8
8
|
License: MIT License
|
9
9
|
|
10
|
-
Copyright (c) 2024 Miquido
|
10
|
+
Copyright (c) 2024-2025 Miquido
|
11
11
|
|
12
12
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
13
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -40,12 +40,12 @@ Requires-Dist: pyright~=1.1; extra == 'dev'
|
|
40
40
|
Requires-Dist: pytest-asyncio~=0.23; extra == 'dev'
|
41
41
|
Requires-Dist: pytest-cov~=4.1; extra == 'dev'
|
42
42
|
Requires-Dist: pytest~=7.4; extra == 'dev'
|
43
|
-
Requires-Dist: ruff~=0.
|
43
|
+
Requires-Dist: ruff~=0.11; extra == 'dev'
|
44
44
|
Description-Content-Type: text/markdown
|
45
45
|
|
46
46
|
# 🚗 haiway 🚕 🚚 🚙
|
47
47
|
|
48
|
-
haiway is a framework
|
48
|
+
haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
|
49
49
|
|
50
50
|
## 🖥️ Install
|
51
51
|
|
@@ -65,7 +65,7 @@ We welcome any feedback and suggestions! Feel free to open an issue or pull requ
|
|
65
65
|
|
66
66
|
MIT License
|
67
67
|
|
68
|
-
Copyright (c) 2024 Miquido
|
68
|
+
Copyright (c) 2024-2025 Miquido
|
69
69
|
|
70
70
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
71
71
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# 🚗 haiway 🚕 🚚 🚙
|
2
2
|
|
3
|
-
haiway is a framework
|
3
|
+
haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
|
4
4
|
|
5
5
|
## 🖥️ Install
|
6
6
|
|
@@ -20,7 +20,7 @@ We welcome any feedback and suggestions! Feel free to open an issue or pull requ
|
|
20
20
|
|
21
21
|
MIT License
|
22
22
|
|
23
|
-
Copyright (c) 2024 Miquido
|
23
|
+
Copyright (c) 2024-2025 Miquido
|
24
24
|
|
25
25
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
26
26
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -12,7 +12,7 @@ COPY ./src/features ./src/features
|
|
12
12
|
COPY ./src/server ./src/server
|
13
13
|
|
14
14
|
# install dependencies and packages
|
15
|
-
COPY --from=ghcr.io/astral-sh/uv:0.
|
15
|
+
COPY --from=ghcr.io/astral-sh/uv:0.6.8 /uv /uvx /bin/
|
16
16
|
|
17
17
|
ENV UV_PROJECT_ENVIRONMENT="/usr/local/"
|
18
18
|
|
@@ -35,3 +35,29 @@ COPY ./config/unit.json /docker-entrypoint.d/config.json
|
|
35
35
|
CMD ["unitd", "--no-daemon", "--log", "/dev/stdout"]
|
36
36
|
|
37
37
|
# port 80 is already exposed by nginx unit image, can't change it...
|
38
|
+
|
39
|
+
# MIGRATIONS #
|
40
|
+
|
41
|
+
FROM python:${PYTHON_TAG} AS migrations_builder
|
42
|
+
|
43
|
+
# copy only the parts needed for production
|
44
|
+
COPY ./src/integrations ./src/integrations
|
45
|
+
COPY ./src/solutions ./src/solutions
|
46
|
+
COPY ./src/features ./src/features
|
47
|
+
COPY ./src/migrations ./src/migrations
|
48
|
+
# install dependencies and packages
|
49
|
+
COPY --from=ghcr.io/astral-sh/uv:0.6.8 /uv /uvx /bin/
|
50
|
+
|
51
|
+
ENV UV_PROJECT_ENVIRONMENT="/usr/local/"
|
52
|
+
|
53
|
+
RUN --mount=type=bind,source=./uv.lock,target=./uv.lock --mount=type=bind,source=./pyproject.toml,target=./pyproject.toml uv sync --python python${PYTHON_TAG} --locked --no-editable --no-python-downloads --link-mode copy --compile-bytecode --only-group server
|
54
|
+
|
55
|
+
FROM migrations_builder AS migrations
|
56
|
+
|
57
|
+
RUN apt-get update \
|
58
|
+
&& apt-get upgrade -y \
|
59
|
+
&& apt-get -y autoremove \
|
60
|
+
&& apt-get clean \
|
61
|
+
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
62
|
+
|
63
|
+
CMD ["python", "-m", "migrations"]
|
@@ -12,7 +12,7 @@ ifndef UV_VERSION
|
|
12
12
|
UV_VERSION := 0.5.14
|
13
13
|
endif
|
14
14
|
|
15
|
-
.PHONY: uv_check venv sync update format lint run docker_run
|
15
|
+
.PHONY: uv_check venv sync update format lint run migrations docker_run
|
16
16
|
|
17
17
|
# Check installed UV version and install if needed
|
18
18
|
uv_check:
|
@@ -81,6 +81,10 @@ lint:
|
|
81
81
|
run:
|
82
82
|
@python -m server
|
83
83
|
|
84
|
-
|
84
|
+
# Execute migrations
|
85
|
+
migrations:
|
86
|
+
@python -m migrations
|
87
|
+
|
88
|
+
# Run all services locally using docker-compose.
|
85
89
|
docker_run:
|
86
90
|
@docker compose up --force-recreate --build server
|
@@ -19,19 +19,19 @@
|
|
19
19
|
"uri": "/*"
|
20
20
|
},
|
21
21
|
"action": {
|
22
|
-
"pass": "applications/
|
22
|
+
"pass": "applications/api"
|
23
23
|
}
|
24
24
|
}
|
25
25
|
]
|
26
26
|
},
|
27
27
|
"applications": {
|
28
|
-
"
|
28
|
+
"api": {
|
29
29
|
"type": "python",
|
30
30
|
"protocol": "asgi",
|
31
31
|
"path": "/",
|
32
32
|
"module": "server",
|
33
33
|
"callable": "app",
|
34
|
-
"processes":
|
34
|
+
"processes": 2,
|
35
35
|
"environment": {
|
36
36
|
"PYTHONOPTIMIZE": "2",
|
37
37
|
"HOME": "."
|
@@ -3,6 +3,8 @@ services:
|
|
3
3
|
depends_on:
|
4
4
|
postgres:
|
5
5
|
condition: service_healthy
|
6
|
+
migrations:
|
7
|
+
condition: service_completed_successfully
|
6
8
|
links:
|
7
9
|
- postgres
|
8
10
|
build:
|
@@ -22,6 +24,27 @@ services:
|
|
22
24
|
POSTGRES_SSLMODE: ${POSTGRES_SSLMODE:-prefer}
|
23
25
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
24
26
|
|
27
|
+
migrations:
|
28
|
+
depends_on:
|
29
|
+
postgres:
|
30
|
+
condition: service_healthy
|
31
|
+
links:
|
32
|
+
- postgres
|
33
|
+
build:
|
34
|
+
context: .
|
35
|
+
dockerfile: Dockerfile
|
36
|
+
target: migrations
|
37
|
+
restart: "no"
|
38
|
+
environment:
|
39
|
+
PYTHONOPTIMIZE: ${PYTHONOPTIMIZE:-2}
|
40
|
+
DEBUG_LOGGING: ${DEBUG_LOGGING}
|
41
|
+
POSTGRES_DATABASE: ${POSTGRES_DATABASE}
|
42
|
+
POSTGRES_HOST: postgres
|
43
|
+
POSTGRES_PORT: 5432
|
44
|
+
POSTGRES_USER: ${POSTGRES_USER}
|
45
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
46
|
+
POSTGRES_SSLMODE: ${POSTGRES_SSLMODE:-prefer}
|
47
|
+
|
25
48
|
postgres:
|
26
49
|
image: postgres:16.3-alpine3.20
|
27
50
|
restart: always
|
@@ -34,7 +57,8 @@ services:
|
|
34
57
|
ports:
|
35
58
|
- ${POSTGRES_PORT}:5432
|
36
59
|
healthcheck:
|
37
|
-
test:
|
60
|
+
test:
|
61
|
+
["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DATABASE}"]
|
38
62
|
interval: 10s
|
39
63
|
start_period: 10s
|
40
64
|
timeout: 10s
|
@@ -28,7 +28,7 @@ server = [
|
|
28
28
|
dev = [
|
29
29
|
"haiway_fastapi",
|
30
30
|
"uvicorn~=0.30",
|
31
|
-
"ruff~=0.
|
31
|
+
"ruff~=0.11",
|
32
32
|
"pyright~=1.1",
|
33
33
|
"bandit~=1.7",
|
34
34
|
]
|
@@ -53,13 +53,10 @@ exclude = ["**/node_modules", "**/__pycache__"]
|
|
53
53
|
ignore = []
|
54
54
|
stubPath = "./stubs"
|
55
55
|
reportMissingImports = true
|
56
|
-
reportMissingTypeStubs =
|
57
|
-
typeCheckingMode = "strict"
|
56
|
+
reportMissingTypeStubs = false
|
58
57
|
userFileIndexingLimit = -1
|
59
58
|
useLibraryCodeForTypes = true
|
60
59
|
|
61
|
-
[tool.
|
62
|
-
|
63
|
-
|
64
|
-
[tool.setuptools.packages.find]
|
65
|
-
where = ["src"]
|
60
|
+
[tool.pyright.analysis]
|
61
|
+
diagnosticMode = "workspace"
|
62
|
+
typeCheckingMode = "strict"
|
@@ -1,4 +1,7 @@
|
|
1
|
+
from uuid import UUID
|
2
|
+
|
1
3
|
from haiway import State
|
4
|
+
from haiway.context import ctx
|
2
5
|
|
3
6
|
from features.todos.types import TodoCompletion
|
4
7
|
from features.todos.user_tasks import complete_todo_task
|
@@ -9,4 +12,12 @@ __all__ = [
|
|
9
12
|
|
10
13
|
|
11
14
|
class Todos(State):
|
15
|
+
@classmethod
|
16
|
+
async def complete_todo(
|
17
|
+
cls,
|
18
|
+
*,
|
19
|
+
identifier: UUID,
|
20
|
+
) -> None:
|
21
|
+
await ctx.state(cls).complete(identifier=identifier)
|
22
|
+
|
12
23
|
complete: TodoCompletion = complete_todo_task
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from uuid import UUID
|
2
|
+
|
3
|
+
from solutions.user_tasks import UserTasks
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
"complete_todo_task",
|
7
|
+
]
|
8
|
+
|
9
|
+
|
10
|
+
async def complete_todo_task(
|
11
|
+
*,
|
12
|
+
identifier: UUID,
|
13
|
+
) -> None:
|
14
|
+
match await UserTasks.fetch(identifier=identifier):
|
15
|
+
case None:
|
16
|
+
pass
|
17
|
+
|
18
|
+
case task:
|
19
|
+
await UserTasks.update(task=task.updated(completed=True))
|
@@ -1,11 +1,11 @@
|
|
1
|
-
from integrations.postgres.client import
|
1
|
+
from integrations.postgres.client import PostgresConnectionPool
|
2
2
|
from integrations.postgres.state import Postgres, PostgresConnection
|
3
3
|
from integrations.postgres.types import PostgresException, PostgresRow, PostgresValue
|
4
4
|
|
5
5
|
__all__ = [
|
6
6
|
"Postgres",
|
7
|
-
"PostgresClient",
|
8
7
|
"PostgresConnection",
|
8
|
+
"PostgresConnectionPool",
|
9
9
|
"PostgresException",
|
10
10
|
"PostgresRow",
|
11
11
|
"PostgresValue",
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections.abc import Sequence
|
1
2
|
from types import TracebackType
|
2
3
|
from typing import final
|
3
4
|
|
@@ -9,18 +10,6 @@ from asyncpg import ( # pyright: ignore[reportMissingTypeStubs]
|
|
9
10
|
from asyncpg.pool import PoolAcquireContext # pyright: ignore[reportMissingTypeStubs]
|
10
11
|
from asyncpg.transaction import Transaction # pyright: ignore[reportMissingTypeStubs]
|
11
12
|
|
12
|
-
from integrations.postgres.state import (
|
13
|
-
PostgresConnection,
|
14
|
-
PostgresConnectionContext,
|
15
|
-
PostgresTransactionContext,
|
16
|
-
)
|
17
|
-
from integrations.postgres.types import PostgresException, PostgresRow, PostgresValue
|
18
|
-
|
19
|
-
__all__ = [
|
20
|
-
"PostgresClient",
|
21
|
-
]
|
22
|
-
|
23
|
-
|
24
13
|
from integrations.postgres.config import (
|
25
14
|
POSTGRES_DATABASE,
|
26
15
|
POSTGRES_HOST,
|
@@ -29,15 +18,36 @@ from integrations.postgres.config import (
|
|
29
18
|
POSTGRES_SSLMODE,
|
30
19
|
POSTGRES_USER,
|
31
20
|
)
|
32
|
-
from integrations.postgres.state import
|
21
|
+
from integrations.postgres.state import (
|
22
|
+
Postgres,
|
23
|
+
PostgresConnection,
|
24
|
+
PostgresConnectionContext,
|
25
|
+
PostgresTransactionContext,
|
26
|
+
)
|
27
|
+
from integrations.postgres.types import (
|
28
|
+
PostgresException,
|
29
|
+
PostgresRow,
|
30
|
+
PostgresValue,
|
31
|
+
)
|
33
32
|
|
34
33
|
__all__ = [
|
35
|
-
"
|
34
|
+
"PostgresConnectionPool",
|
36
35
|
]
|
37
36
|
|
38
37
|
|
39
38
|
@final
|
40
|
-
class
|
39
|
+
class PostgresConnectionPool:
|
40
|
+
__slots__ = (
|
41
|
+
"_connection_limit",
|
42
|
+
"_database",
|
43
|
+
"_host",
|
44
|
+
"_password",
|
45
|
+
"_pool",
|
46
|
+
"_port",
|
47
|
+
"_ssl",
|
48
|
+
"_user",
|
49
|
+
)
|
50
|
+
|
41
51
|
def __init__( # noqa: PLR0913
|
42
52
|
self,
|
43
53
|
host: str = POSTGRES_HOST,
|
@@ -48,20 +58,28 @@ class PostgresClient:
|
|
48
58
|
ssl: str = POSTGRES_SSLMODE,
|
49
59
|
connection_limit: int = 1,
|
50
60
|
) -> None:
|
51
|
-
self.
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
ssl=ssl,
|
60
|
-
)
|
61
|
+
self._host: str = host
|
62
|
+
self._port: str = port
|
63
|
+
self._database: str = database
|
64
|
+
self._user: str = user
|
65
|
+
self._password: str = password
|
66
|
+
self._ssl: str = ssl
|
67
|
+
self._connection_limit: int = connection_limit
|
68
|
+
self._pool: Pool
|
61
69
|
|
62
70
|
async def __aenter__(self) -> Postgres:
|
71
|
+
self._pool = create_pool(
|
72
|
+
min_size=1,
|
73
|
+
max_size=self._connection_limit,
|
74
|
+
database=self._database,
|
75
|
+
user=self._user,
|
76
|
+
password=self._password,
|
77
|
+
host=self._host,
|
78
|
+
port=self._port,
|
79
|
+
ssl=self._ssl,
|
80
|
+
)
|
63
81
|
await self._pool # initialize pool
|
64
|
-
return Postgres(
|
82
|
+
return Postgres(prepare_connection=self.prepare_connection)
|
65
83
|
|
66
84
|
async def __aexit__(
|
67
85
|
self,
|
@@ -72,12 +90,14 @@ class PostgresClient:
|
|
72
90
|
if self._pool._initialized: # pyright: ignore[reportPrivateUsage]
|
73
91
|
await self._pool.close()
|
74
92
|
|
75
|
-
def
|
93
|
+
def prepare_connection(self) -> PostgresConnectionContext:
|
76
94
|
return _ConnectionContext(pool_context=self._pool.acquire()) # pyright: ignore[reportUnknownMemberType]
|
77
95
|
|
78
96
|
|
79
97
|
@final
|
80
98
|
class _TransactionContext:
|
99
|
+
__slots__ = ("_transaction_context",)
|
100
|
+
|
81
101
|
def __init__(
|
82
102
|
self,
|
83
103
|
transaction_context: Transaction,
|
@@ -102,6 +122,8 @@ class _TransactionContext:
|
|
102
122
|
|
103
123
|
@final
|
104
124
|
class _ConnectionContext:
|
125
|
+
__slots__ = ("_pool_context",)
|
126
|
+
|
105
127
|
def __init__(
|
106
128
|
self,
|
107
129
|
pool_context: PoolAcquireContext,
|
@@ -115,12 +137,15 @@ class _ConnectionContext:
|
|
115
137
|
statement: str,
|
116
138
|
/,
|
117
139
|
*args: PostgresValue,
|
118
|
-
) ->
|
140
|
+
) -> Sequence[PostgresRow]:
|
119
141
|
try:
|
120
|
-
return
|
121
|
-
|
122
|
-
|
123
|
-
|
142
|
+
return [
|
143
|
+
dict(record) # convert to dict to allow match patterns
|
144
|
+
for record in await acquired_connection.fetch( # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
145
|
+
statement,
|
146
|
+
*args,
|
147
|
+
)
|
148
|
+
]
|
124
149
|
|
125
150
|
except Exception as exc:
|
126
151
|
raise PostgresException("Failed to execute SQL statement") from exc
|
@@ -129,8 +154,8 @@ class _ConnectionContext:
|
|
129
154
|
return _TransactionContext(transaction_context=acquired_connection.transaction()) # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType]
|
130
155
|
|
131
156
|
return PostgresConnection(
|
132
|
-
|
133
|
-
|
157
|
+
execute_statement=execute,
|
158
|
+
prepare_transaction=transaction,
|
134
159
|
)
|
135
160
|
|
136
161
|
async def __aexit__(
|
@@ -0,0 +1,77 @@
|
|
1
|
+
from collections.abc import Sequence
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from haiway import State, ctx
|
5
|
+
|
6
|
+
from integrations.postgres.types import (
|
7
|
+
PostgresConnectionContext,
|
8
|
+
PostgresConnectionPreparing,
|
9
|
+
PostgresRow,
|
10
|
+
PostgresStatementExecuting,
|
11
|
+
PostgresTransactionContext,
|
12
|
+
PostgresTransactionPreparing,
|
13
|
+
)
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
"Postgres",
|
17
|
+
"PostgresConnection",
|
18
|
+
]
|
19
|
+
|
20
|
+
|
21
|
+
class PostgresConnection(State):
|
22
|
+
execute_statement: PostgresStatementExecuting
|
23
|
+
prepare_transaction: PostgresTransactionPreparing
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
async def fetch_one(
|
27
|
+
cls,
|
28
|
+
statement: str,
|
29
|
+
/,
|
30
|
+
*args: Any,
|
31
|
+
) -> PostgresRow | None:
|
32
|
+
return next(
|
33
|
+
(
|
34
|
+
result
|
35
|
+
for result in await ctx.state(cls).execute_statement(
|
36
|
+
statement,
|
37
|
+
*args,
|
38
|
+
)
|
39
|
+
),
|
40
|
+
None,
|
41
|
+
)
|
42
|
+
|
43
|
+
@classmethod
|
44
|
+
async def fetch(
|
45
|
+
cls,
|
46
|
+
statement: str,
|
47
|
+
/,
|
48
|
+
*args: Any,
|
49
|
+
) -> Sequence[PostgresRow]:
|
50
|
+
return await ctx.state(cls).execute_statement(
|
51
|
+
statement,
|
52
|
+
*args,
|
53
|
+
)
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
async def execute(
|
57
|
+
cls,
|
58
|
+
statement: str,
|
59
|
+
/,
|
60
|
+
*args: Any,
|
61
|
+
) -> None:
|
62
|
+
await ctx.state(cls).execute_statement(
|
63
|
+
statement,
|
64
|
+
*args,
|
65
|
+
)
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def transaction(cls) -> PostgresTransactionContext:
|
69
|
+
return ctx.state(cls).prepare_transaction()
|
70
|
+
|
71
|
+
|
72
|
+
class Postgres(State):
|
73
|
+
@classmethod
|
74
|
+
def connection(cls) -> PostgresConnectionContext:
|
75
|
+
return ctx.state(cls).prepare_connection()
|
76
|
+
|
77
|
+
prepare_connection: PostgresConnectionPreparing
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
2
|
+
from datetime import date, datetime, time
|
3
|
+
from types import TracebackType
|
4
|
+
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
5
|
+
from uuid import UUID
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from integrations.postgres.state import PostgresConnection
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"PostgresConnectionContext",
|
12
|
+
"PostgresConnectionPreparing",
|
13
|
+
"PostgresException",
|
14
|
+
"PostgresStatementExecuting",
|
15
|
+
"PostgresTransactionContext",
|
16
|
+
"PostgresTransactionPreparing",
|
17
|
+
"PostgresValue",
|
18
|
+
]
|
19
|
+
|
20
|
+
type PostgresValue = UUID | datetime | date | time | str | bytes | float | int | bool | None
|
21
|
+
type PostgresRow = Mapping[str, Any]
|
22
|
+
|
23
|
+
|
24
|
+
@runtime_checkable
|
25
|
+
class PostgresStatementExecuting(Protocol):
|
26
|
+
async def __call__(
|
27
|
+
self,
|
28
|
+
statement: str,
|
29
|
+
/,
|
30
|
+
*args: PostgresValue,
|
31
|
+
) -> Sequence[PostgresRow]: ...
|
32
|
+
|
33
|
+
|
34
|
+
@runtime_checkable
|
35
|
+
class PostgresTransactionContext(Protocol):
|
36
|
+
async def __aenter__(self) -> None: ...
|
37
|
+
|
38
|
+
async def __aexit__(
|
39
|
+
self,
|
40
|
+
exc_type: type[BaseException] | None,
|
41
|
+
exc_val: BaseException | None,
|
42
|
+
exc_tb: TracebackType | None,
|
43
|
+
) -> bool | None: ...
|
44
|
+
|
45
|
+
|
46
|
+
@runtime_checkable
|
47
|
+
class PostgresTransactionPreparing(Protocol):
|
48
|
+
def __call__(self) -> PostgresTransactionContext: ...
|
49
|
+
|
50
|
+
|
51
|
+
@runtime_checkable
|
52
|
+
class PostgresConnectionContext(Protocol):
|
53
|
+
async def __aenter__(self) -> "PostgresConnection": ...
|
54
|
+
|
55
|
+
async def __aexit__(
|
56
|
+
self,
|
57
|
+
exc_type: type[BaseException] | None,
|
58
|
+
exc_val: BaseException | None,
|
59
|
+
exc_tb: TracebackType | None,
|
60
|
+
) -> bool | None: ...
|
61
|
+
|
62
|
+
|
63
|
+
@runtime_checkable
|
64
|
+
class PostgresConnectionPreparing(Protocol):
|
65
|
+
def __call__(self) -> PostgresConnectionContext: ...
|
66
|
+
|
67
|
+
|
68
|
+
class PostgresException(Exception):
|
69
|
+
pass
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from asyncio import run
|
2
|
+
|
3
|
+
from haiway import ctx, setup_logging
|
4
|
+
|
5
|
+
from integrations.postgres import PostgresConnectionPool
|
6
|
+
from migrations.postgres import execute_postgres_migrations
|
7
|
+
|
8
|
+
|
9
|
+
async def migrate_databases() -> None:
|
10
|
+
async with ctx.scope(
|
11
|
+
"migrations",
|
12
|
+
disposables=[
|
13
|
+
PostgresConnectionPool(),
|
14
|
+
],
|
15
|
+
):
|
16
|
+
ctx.log_warning("Running postgres migrations...")
|
17
|
+
await execute_postgres_migrations()
|
18
|
+
ctx.log_info("...postgres migrations completed")
|
19
|
+
|
20
|
+
|
21
|
+
def main() -> None:
|
22
|
+
setup_logging("migrations")
|
23
|
+
run(migrate_databases())
|
24
|
+
|
25
|
+
|
26
|
+
main()
|