python-neva 3.6.0__tar.gz → 3.7.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.
- python_neva-3.7.0/.gitlab-ci.yml +79 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/.pre-commit-config.yaml +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/CHANGELOG.md +18 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/PKG-INFO +9 -5
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/connection.py +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/transaction.py +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/strategy.py +2 -2
- {python_neva-3.6.0 → python_neva-3.7.0}/pyproject.toml +9 -5
- python_neva-3.7.0/ruff.toml +47 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_registration.py +2 -6
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/test_loader.py +2 -6
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/conftest.py +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_listen_on_parent_class.py +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/test_encrypter.py +1 -1
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/test_hash_manager.py +8 -8
- python_neva-3.7.0/uv.lock +1855 -0
- python_neva-3.6.0/ruff.toml +0 -43
- python_neva-3.6.0/uv.lock +0 -2293
- {python_neva-3.6.0 → python_neva-3.7.0}/.claude/settings.local.json +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/.envrc +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/.gitignore +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/.python-version +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/CLAUDE.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/README.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/application.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/config.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/facade.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/integrations/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/integrations/faststream.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/markers.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/scopes.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/service_provider.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/base_providers.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/loader.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/repository.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/config.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/manager.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/provider.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/dispatcher.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/event.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/handler.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/listener.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/dispatcher.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/event.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/event_registry.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/listener.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/policy.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/provider.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/instrumentation/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/instrumentation/sqlalchemy.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/manager.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/provider.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/correlation.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/profiler.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/factories.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/persistence.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/encrypter.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/protocol.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/config.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hash_manager.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/argon2.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/bcrypt.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/protocol.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/provider.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/generate_token.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/hash_token.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/verify_token.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/accessors.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/app.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/app.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/config.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/config.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/crypt.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/crypt.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/db.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/db.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/event.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/event.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/hash.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/hash.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/log.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/log.pyi +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/results.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/strconv.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/time.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/fakes.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/fixtures.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/py.typed +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/test_case.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/scripts/retag-with-changelog.sh +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_cache.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_context.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_extends.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_lifetimes.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_scope.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/test_repository.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/conftest.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_connection_manager.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_database_manager.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_detached_lifespan.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_edge_cases.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_multi_connection.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_sqlalchemy_integration.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction_context.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction_registry.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_before_dispatch.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_binding.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_deferred.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_dispatch.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_event.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_function_listener.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_immediate.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/test_correlation.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/test_profiler.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/__init__.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_event_fake.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_facade_restore.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_fixtures.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_refresh_database.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_test_case.py +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/KNOWN-DISCREPANCIES.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/configuration.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/database-and-transactions.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/dependency-injection.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/events.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/result-option.md +0 -0
- {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/testing.md +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
variables:
|
|
3
|
+
PYTHON_VERSION: "3.12"
|
|
4
|
+
UV_VERSION: "0.9.28"
|
|
5
|
+
BASE_LAYER: bookworm-slim
|
|
6
|
+
UV_LINK_MODE: copy
|
|
7
|
+
UV_CACHE_DIR: .cache/uv
|
|
8
|
+
|
|
9
|
+
stages:
|
|
10
|
+
- code_qa
|
|
11
|
+
- test
|
|
12
|
+
- deps
|
|
13
|
+
|
|
14
|
+
##### Deps #####
|
|
15
|
+
.uv_base:
|
|
16
|
+
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
|
17
|
+
cache:
|
|
18
|
+
key:
|
|
19
|
+
files:
|
|
20
|
+
- uv.lock
|
|
21
|
+
paths:
|
|
22
|
+
- .cache/uv
|
|
23
|
+
- .venv
|
|
24
|
+
before_script:
|
|
25
|
+
- uv sync --frozen --all-extras
|
|
26
|
+
|
|
27
|
+
.default_rules:
|
|
28
|
+
rules:
|
|
29
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
30
|
+
- if: $CI_COMMIT_BRANCH == "main"
|
|
31
|
+
|
|
32
|
+
##### Code QA #####
|
|
33
|
+
ruff:lint:
|
|
34
|
+
extends: [.uv_base, .default_rules]
|
|
35
|
+
stage: code_qa
|
|
36
|
+
script:
|
|
37
|
+
- uv run --frozen ruff check .
|
|
38
|
+
|
|
39
|
+
ruff:format:
|
|
40
|
+
extends: [.uv_base, .default_rules]
|
|
41
|
+
stage: code_qa
|
|
42
|
+
script:
|
|
43
|
+
- uv run --frozen ruff format --check .
|
|
44
|
+
|
|
45
|
+
pyrefly:
|
|
46
|
+
extends: [.uv_base, .default_rules]
|
|
47
|
+
stage: code_qa
|
|
48
|
+
script:
|
|
49
|
+
- uv run --frozen pyrefly check
|
|
50
|
+
|
|
51
|
+
##### Test #####
|
|
52
|
+
pytest:
|
|
53
|
+
extends: [.uv_base, .default_rules]
|
|
54
|
+
stage: test
|
|
55
|
+
script:
|
|
56
|
+
- uv run --frozen pytest
|
|
57
|
+
|
|
58
|
+
##### Deps contract #####
|
|
59
|
+
# Resolves every direct dependency at its declared floor, so a `>=` lower than
|
|
60
|
+
# what the code needs fails here instead of in a user's install. Three things
|
|
61
|
+
# keep this job honest, and dropping any one makes it pass without testing
|
|
62
|
+
# anything: it must not extend .uv_base, whose before_script installs the
|
|
63
|
+
# committed lock; --upgrade discards that lock as a preference set, without
|
|
64
|
+
# which uv reuses it and lowers nothing; --frozen on sync and run stops uv
|
|
65
|
+
# re-resolving at the default highest strategy and discarding the floors it
|
|
66
|
+
# just computed. Rewriting uv.lock here is expected and must never be
|
|
67
|
+
# committed.
|
|
68
|
+
deps:floors:
|
|
69
|
+
extends: .default_rules
|
|
70
|
+
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
|
71
|
+
stage: deps
|
|
72
|
+
cache:
|
|
73
|
+
key: floors
|
|
74
|
+
paths:
|
|
75
|
+
- .cache/uv
|
|
76
|
+
script:
|
|
77
|
+
- uv lock --upgrade --resolution lowest-direct
|
|
78
|
+
- uv sync --frozen --all-extras
|
|
79
|
+
- uv run --frozen pytest
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 3.7.0 (2026-08-28)
|
|
2
|
+
|
|
3
|
+
### ✨ Features
|
|
4
|
+
|
|
5
|
+
- **deps**: require only what the core imports
|
|
6
|
+
|
|
7
|
+
### 🐛🚑️ Fixes
|
|
8
|
+
|
|
9
|
+
- **deps**: declare starlette directly
|
|
10
|
+
|
|
11
|
+
### 💚👷 CI & Build
|
|
12
|
+
|
|
13
|
+
- add the pipeline with a lowest-direct floors gate
|
|
14
|
+
|
|
15
|
+
### 📌➕⬇️➖⬆️ Dependencies
|
|
16
|
+
|
|
17
|
+
- upgrade all dependencies and adapt to ruff 0.16
|
|
18
|
+
|
|
1
19
|
## 3.6.0 (2026-08-28)
|
|
2
20
|
|
|
3
21
|
### ✨ Features
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-neva
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: aiosqlite>=0.20.0
|
|
7
7
|
Requires-Dist: asyncpg>=0.30.0
|
|
8
8
|
Requires-Dist: cryptography>=46.0.3
|
|
9
9
|
Requires-Dist: dishka>=1.10.0
|
|
10
|
-
Requires-Dist: fastapi[all]>=0.129.0
|
|
11
|
-
Requires-Dist: faststream>=0.6.6
|
|
12
10
|
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.62b0
|
|
13
11
|
Requires-Dist: pwdlib[argon2,bcrypt]>=0.3.0
|
|
12
|
+
Requires-Dist: pydantic>=2.7
|
|
14
13
|
Requires-Dist: pyinstrument>=5.1.1
|
|
15
14
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.0
|
|
15
|
+
Requires-Dist: starlette>=0.41
|
|
16
16
|
Requires-Dist: structlog>=25.5.0
|
|
17
|
-
Requires-Dist:
|
|
17
|
+
Requires-Dist: typing-extensions>=4.13
|
|
18
18
|
Provides-Extra: fastapi
|
|
19
|
-
Requires-Dist: neva-fastapi>=1.
|
|
19
|
+
Requires-Dist: neva-fastapi>=1.1.1; extra == 'fastapi'
|
|
20
|
+
Provides-Extra: faststream
|
|
21
|
+
Requires-Dist: faststream>=0.6.6; extra == 'faststream'
|
|
22
|
+
Provides-Extra: polyfactory
|
|
23
|
+
Requires-Dist: polyfactory>=3.1.0; extra == 'polyfactory'
|
|
20
24
|
Provides-Extra: testing
|
|
21
25
|
Requires-Dist: pytest-asyncio>=0.25.3; extra == 'testing'
|
|
22
26
|
Requires-Dist: pytest>=9.0.2; extra == 'testing'
|
|
@@ -134,7 +134,7 @@ class BoundTransaction(Transaction):
|
|
|
134
134
|
|
|
135
135
|
@property
|
|
136
136
|
def is_accessible_from_current_task(self) -> bool:
|
|
137
|
-
"""
|
|
137
|
+
"""Whether the current asyncio task may use this transaction's session.
|
|
138
138
|
|
|
139
139
|
Returns:
|
|
140
140
|
True if the transaction was created by the current task, or if
|
|
@@ -74,12 +74,12 @@ class StrategyResolver[T](ABC):
|
|
|
74
74
|
|
|
75
75
|
@property
|
|
76
76
|
def strategies(self) -> dict[str, T]:
|
|
77
|
-
"""
|
|
77
|
+
"""The created drivers."""
|
|
78
78
|
return self._strategies
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
81
|
def app(self) -> Application:
|
|
82
|
-
"""
|
|
82
|
+
"""The underlying container."""
|
|
83
83
|
return self._app
|
|
84
84
|
|
|
85
85
|
def set_container(self, app: Application) -> None:
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-neva"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.7.0"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"cryptography>=46.0.3",
|
|
9
9
|
"dishka>=1.10.0",
|
|
10
|
-
"fastapi[all]>=0.129.0",
|
|
11
|
-
"faststream>=0.6.6",
|
|
12
10
|
"pwdlib[argon2,bcrypt]>=0.3.0",
|
|
11
|
+
"pydantic>=2.7",
|
|
13
12
|
"pyinstrument>=5.1.1",
|
|
14
13
|
"structlog>=25.5.0",
|
|
15
14
|
"sqlalchemy[asyncio]>=2.0.0",
|
|
15
|
+
"starlette>=0.41",
|
|
16
|
+
"typing-extensions>=4.13",
|
|
16
17
|
"asyncpg>=0.30.0",
|
|
17
18
|
"aiosqlite>=0.20.0",
|
|
18
|
-
"typer>=0.21.1",
|
|
19
19
|
"opentelemetry-instrumentation-sqlalchemy>=0.62b0",
|
|
20
20
|
]
|
|
21
21
|
|
|
@@ -32,12 +32,16 @@ dev = [
|
|
|
32
32
|
"pytest-asyncio>=0.25.3",
|
|
33
33
|
"pytest-benchmark>=5.2.3",
|
|
34
34
|
"pytest-cov>=7.0.0",
|
|
35
|
+
"httpx>=0.27",
|
|
36
|
+
"httpx2>=0.1.0",
|
|
35
37
|
"ruff>=0.15.6",
|
|
36
38
|
]
|
|
37
39
|
|
|
38
40
|
[project.optional-dependencies]
|
|
39
41
|
testing = ["pytest>=9.0.2", "pytest-asyncio>=0.25.3"]
|
|
40
|
-
fastapi = ["neva-fastapi>=1.
|
|
42
|
+
fastapi = ["neva-fastapi>=1.1.1"]
|
|
43
|
+
faststream = ["faststream>=0.6.6"]
|
|
44
|
+
polyfactory = ["polyfactory>=3.1.0"]
|
|
41
45
|
|
|
42
46
|
[build-system]
|
|
43
47
|
requires = ["hatchling"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
target-version = "py312"
|
|
2
|
+
line-length = 88
|
|
3
|
+
|
|
4
|
+
[lint]
|
|
5
|
+
select = [
|
|
6
|
+
"ANN",
|
|
7
|
+
"ASYNC",
|
|
8
|
+
"S",
|
|
9
|
+
"FBT",
|
|
10
|
+
"B",
|
|
11
|
+
"A",
|
|
12
|
+
"C4",
|
|
13
|
+
"DTZ",
|
|
14
|
+
"ISC",
|
|
15
|
+
"ICN",
|
|
16
|
+
"SIM",
|
|
17
|
+
"SLOT",
|
|
18
|
+
"I",
|
|
19
|
+
"E",
|
|
20
|
+
"W",
|
|
21
|
+
"DOC",
|
|
22
|
+
"D",
|
|
23
|
+
"F",
|
|
24
|
+
"RUF",
|
|
25
|
+
]
|
|
26
|
+
preview = true
|
|
27
|
+
ignore = [
|
|
28
|
+
"explicit-string-concatenation", # enforce explicit string concatenation
|
|
29
|
+
"undocumented-public-init", # Allow missing docstrings in __init__
|
|
30
|
+
"undocumented-public-module", # Allow missing docstrings in public modules
|
|
31
|
+
"unused-async",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[lint.per-file-ignores]
|
|
35
|
+
"tests/*" = ["D", "assert"]
|
|
36
|
+
|
|
37
|
+
[lint.isort]
|
|
38
|
+
force-single-line = false
|
|
39
|
+
lines-after-imports = 2
|
|
40
|
+
|
|
41
|
+
[lint.pydocstyle]
|
|
42
|
+
convention = "google"
|
|
43
|
+
|
|
44
|
+
[format]
|
|
45
|
+
# ruff 0.16 formats Python blocks inside Markdown; the wiki guides rely on
|
|
46
|
+
# hand-aligned trailing comments the formatter would re-flow.
|
|
47
|
+
exclude = ["*.md"]
|
|
@@ -98,9 +98,7 @@ class TestRegisterAfterBoot:
|
|
|
98
98
|
async with app.lifespan():
|
|
99
99
|
assert app.register(WidgetProvider).is_ok
|
|
100
100
|
|
|
101
|
-
async def test_registration_reopens_after_shutdown(
|
|
102
|
-
self, app: Application
|
|
103
|
-
) -> None:
|
|
101
|
+
async def test_registration_reopens_after_shutdown(self, app: Application) -> None:
|
|
104
102
|
async with app.lifespan():
|
|
105
103
|
pass
|
|
106
104
|
|
|
@@ -137,9 +135,7 @@ class TestLifespanCleanup:
|
|
|
137
135
|
|
|
138
136
|
assert torn_down == ["resource"]
|
|
139
137
|
|
|
140
|
-
async def test_facade_is_unset_when_the_body_raises(
|
|
141
|
-
self, app: Application
|
|
142
|
-
) -> None:
|
|
138
|
+
async def test_facade_is_unset_when_the_body_raises(self, app: Application) -> None:
|
|
143
139
|
with pytest.raises(RuntimeError, match="boom"):
|
|
144
140
|
async with app.lifespan():
|
|
145
141
|
raise RuntimeError("boom")
|
|
@@ -101,9 +101,7 @@ class TestLoadAll:
|
|
|
101
101
|
class TestModuleNamespacing:
|
|
102
102
|
"""Config files are data, and must not displace real modules."""
|
|
103
103
|
|
|
104
|
-
def test_a_config_file_does_not_shadow_a_core_module(
|
|
105
|
-
self, tmp_path: Path
|
|
106
|
-
) -> None:
|
|
104
|
+
def test_a_config_file_does_not_shadow_a_core_module(self, tmp_path: Path) -> None:
|
|
107
105
|
config_dir = tmp_path / "config"
|
|
108
106
|
config_dir.mkdir()
|
|
109
107
|
_ = (config_dir / "repository.py").write_text("""config = {"x": 1}""")
|
|
@@ -117,9 +115,7 @@ class TestModuleNamespacing:
|
|
|
117
115
|
config_dir / "repository.py"
|
|
118
116
|
)
|
|
119
117
|
|
|
120
|
-
def test_config_modules_are_not_left_in_sys_modules(
|
|
121
|
-
self, tmp_path: Path
|
|
122
|
-
) -> None:
|
|
118
|
+
def test_config_modules_are_not_left_in_sys_modules(self, tmp_path: Path) -> None:
|
|
123
119
|
config_dir = tmp_path / "config"
|
|
124
120
|
config_dir.mkdir()
|
|
125
121
|
_ = (config_dir / "app.py").write_text("""config = {"name": "X"}""")
|
|
@@ -16,7 +16,7 @@ from neva.events.event_registry import EventRegistry
|
|
|
16
16
|
class OrderPlaced(Event[int]):
|
|
17
17
|
"""An immediate event used across test modules."""
|
|
18
18
|
|
|
19
|
-
id: int = pydantic.Field(default=randint(0, 200)) #
|
|
19
|
+
id: int = pydantic.Field(default=randint(0, 200)) # ruff: ignore[suspicious-non-cryptographic-random-usage]
|
|
20
20
|
order_id: int
|
|
21
21
|
|
|
22
22
|
|
|
@@ -8,7 +8,7 @@ from neva.support import Ok, Result
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class OrderEvent(Event[int]):
|
|
11
|
-
id: int = pydantic.Field(default=randint(0, 200)) #
|
|
11
|
+
id: int = pydantic.Field(default=randint(0, 200)) # ruff: ignore[suspicious-non-cryptographic-random-usage]
|
|
12
12
|
order_id: int
|
|
13
13
|
|
|
14
14
|
|
|
@@ -70,7 +70,7 @@ class TestEncryptDecryptRoundTrip(TestCase):
|
|
|
70
70
|
assert math.isclose(cast(float, decrypted), 3.14)
|
|
71
71
|
|
|
72
72
|
async def test_encrypt_decrypt_boolean(self) -> None:
|
|
73
|
-
encrypted = Crypt.encrypt(True).unwrap() #
|
|
73
|
+
encrypted = Crypt.encrypt(True).unwrap() # ruff: ignore[boolean-positional-value-in-call]
|
|
74
74
|
decrypted = Crypt.decrypt(encrypted).unwrap()
|
|
75
75
|
|
|
76
76
|
assert decrypted is True
|
|
@@ -52,22 +52,22 @@ config = {
|
|
|
52
52
|
return config_dir
|
|
53
53
|
|
|
54
54
|
async def test_hash_manager_creates_with_default_driver(self) -> None:
|
|
55
|
-
password = "secret_password" #
|
|
55
|
+
password = "secret_password" # ruff: ignore[hardcoded-password-string]
|
|
56
56
|
hashed = Hash.make(password)
|
|
57
57
|
|
|
58
58
|
assert hashed.startswith("$2b$")
|
|
59
59
|
assert Hash.check(password, hashed)
|
|
60
60
|
|
|
61
61
|
async def test_hash_manager_can_use_specific_hasher(self) -> None:
|
|
62
|
-
password = "another_password" #
|
|
62
|
+
password = "another_password" # ruff: ignore[hardcoded-password-string]
|
|
63
63
|
hashed = Hash.make(password, hasher="argon2")
|
|
64
64
|
|
|
65
65
|
assert hashed.startswith("$argon2")
|
|
66
66
|
assert Hash.check(password, hashed, hasher="argon2")
|
|
67
67
|
|
|
68
68
|
async def test_hash_manager_check_password(self) -> None:
|
|
69
|
-
password = "test_password" #
|
|
70
|
-
wrong_password = "wrong_password" #
|
|
69
|
+
password = "test_password" # ruff: ignore[hardcoded-password-string]
|
|
70
|
+
wrong_password = "wrong_password" # ruff: ignore[hardcoded-password-string]
|
|
71
71
|
|
|
72
72
|
hashed = Hash.make(password)
|
|
73
73
|
|
|
@@ -75,7 +75,7 @@ config = {
|
|
|
75
75
|
assert not Hash.check(wrong_password, hashed)
|
|
76
76
|
|
|
77
77
|
async def test_hash_manager_needs_rehash(self) -> None:
|
|
78
|
-
password = "test_password" #
|
|
78
|
+
password = "test_password" # ruff: ignore[hardcoded-password-string]
|
|
79
79
|
hashed = Hash.make(password)
|
|
80
80
|
|
|
81
81
|
assert not Hash.needs_rehash(hashed)
|
|
@@ -116,7 +116,7 @@ config = {"providers": [SecurityProvider]}
|
|
|
116
116
|
return config_dir
|
|
117
117
|
|
|
118
118
|
async def test_bcrypt_works_without_a_bcrypt_namespace(self) -> None:
|
|
119
|
-
password = "secret_password" #
|
|
119
|
+
password = "secret_password" # ruff: ignore[hardcoded-password-string]
|
|
120
120
|
|
|
121
121
|
hashed = Hash.make(password)
|
|
122
122
|
|
|
@@ -124,7 +124,7 @@ config = {"providers": [SecurityProvider]}
|
|
|
124
124
|
assert Hash.check(password, hashed)
|
|
125
125
|
|
|
126
126
|
async def test_argon2_works_without_an_argon_namespace(self) -> None:
|
|
127
|
-
password = "secret_password" #
|
|
127
|
+
password = "secret_password" # ruff: ignore[hardcoded-password-string]
|
|
128
128
|
|
|
129
129
|
hashed = Hash.make(password, hasher="argon2")
|
|
130
130
|
|
|
@@ -168,7 +168,7 @@ config = {"providers": [SecurityProvider]}
|
|
|
168
168
|
return config_dir
|
|
169
169
|
|
|
170
170
|
async def test_make_async_round_trips(self) -> None:
|
|
171
|
-
password = "secret_password" #
|
|
171
|
+
password = "secret_password" # ruff: ignore[hardcoded-password-string]
|
|
172
172
|
|
|
173
173
|
hashed = await Hash.make_async(password)
|
|
174
174
|
|