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.
Files changed (160) hide show
  1. python_neva-3.7.0/.gitlab-ci.yml +79 -0
  2. {python_neva-3.6.0 → python_neva-3.7.0}/.pre-commit-config.yaml +1 -1
  3. {python_neva-3.6.0 → python_neva-3.7.0}/CHANGELOG.md +18 -0
  4. {python_neva-3.6.0 → python_neva-3.7.0}/PKG-INFO +9 -5
  5. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/connection.py +1 -1
  6. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/transaction.py +1 -1
  7. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/strategy.py +2 -2
  8. {python_neva-3.6.0 → python_neva-3.7.0}/pyproject.toml +9 -5
  9. python_neva-3.7.0/ruff.toml +47 -0
  10. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_registration.py +2 -6
  11. {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/test_loader.py +2 -6
  12. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/conftest.py +1 -1
  13. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_listen_on_parent_class.py +1 -1
  14. {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/test_encrypter.py +1 -1
  15. {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/test_hash_manager.py +8 -8
  16. python_neva-3.7.0/uv.lock +1855 -0
  17. python_neva-3.6.0/ruff.toml +0 -43
  18. python_neva-3.6.0/uv.lock +0 -2293
  19. {python_neva-3.6.0 → python_neva-3.7.0}/.claude/settings.local.json +0 -0
  20. {python_neva-3.6.0 → python_neva-3.7.0}/.envrc +0 -0
  21. {python_neva-3.6.0 → python_neva-3.7.0}/.gitignore +0 -0
  22. {python_neva-3.6.0 → python_neva-3.7.0}/.python-version +0 -0
  23. {python_neva-3.6.0 → python_neva-3.7.0}/CLAUDE.md +0 -0
  24. {python_neva-3.6.0 → python_neva-3.7.0}/README.md +0 -0
  25. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/__init__.py +0 -0
  26. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/application.py +0 -0
  27. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/config.py +0 -0
  28. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/facade.py +0 -0
  29. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/integrations/__init__.py +0 -0
  30. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/integrations/faststream.py +0 -0
  31. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/markers.py +0 -0
  32. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/py.typed +0 -0
  33. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/scopes.py +0 -0
  34. {python_neva-3.6.0 → python_neva-3.7.0}/neva/arch/service_provider.py +0 -0
  35. {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/__init__.py +0 -0
  36. {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/base_providers.py +0 -0
  37. {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/loader.py +0 -0
  38. {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/py.typed +0 -0
  39. {python_neva-3.6.0 → python_neva-3.7.0}/neva/config/repository.py +0 -0
  40. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/__init__.py +0 -0
  41. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/config.py +0 -0
  42. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/manager.py +0 -0
  43. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/provider.py +0 -0
  44. {python_neva-3.6.0 → python_neva-3.7.0}/neva/database/py.typed +0 -0
  45. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/__init__.py +0 -0
  46. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/__init__.py +0 -0
  47. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/dispatcher.py +0 -0
  48. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/event.py +0 -0
  49. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/handler.py +0 -0
  50. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/contracts/listener.py +0 -0
  51. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/dispatcher.py +0 -0
  52. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/event.py +0 -0
  53. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/event_registry.py +0 -0
  54. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/listener.py +0 -0
  55. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/policy.py +0 -0
  56. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/provider.py +0 -0
  57. {python_neva-3.6.0 → python_neva-3.7.0}/neva/events/py.typed +0 -0
  58. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/__init__.py +0 -0
  59. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/instrumentation/__init__.py +0 -0
  60. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/instrumentation/sqlalchemy.py +0 -0
  61. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/__init__.py +0 -0
  62. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/manager.py +0 -0
  63. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/logging/provider.py +0 -0
  64. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/__init__.py +0 -0
  65. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/correlation.py +0 -0
  66. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/middleware/profiler.py +0 -0
  67. {python_neva-3.6.0 → python_neva-3.7.0}/neva/obs/py.typed +0 -0
  68. {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/__init__.py +0 -0
  69. {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/factories.py +0 -0
  70. {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/persistence.py +0 -0
  71. {python_neva-3.6.0 → python_neva-3.7.0}/neva/polyfactory/py.typed +0 -0
  72. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/__init__.py +0 -0
  73. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/__init__.py +0 -0
  74. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/encrypter.py +0 -0
  75. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/encryption/protocol.py +0 -0
  76. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/__init__.py +0 -0
  77. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/config.py +0 -0
  78. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hash_manager.py +0 -0
  79. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/__init__.py +0 -0
  80. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/argon2.py +0 -0
  81. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/bcrypt.py +0 -0
  82. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/hashing/hashers/protocol.py +0 -0
  83. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/provider.py +0 -0
  84. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/py.typed +0 -0
  85. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/__init__.py +0 -0
  86. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/generate_token.py +0 -0
  87. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/hash_token.py +0 -0
  88. {python_neva-3.6.0 → python_neva-3.7.0}/neva/security/tokens/verify_token.py +0 -0
  89. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/__init__.py +0 -0
  90. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/accessors.py +0 -0
  91. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/__init__.py +0 -0
  92. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/app.py +0 -0
  93. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/app.pyi +0 -0
  94. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/config.py +0 -0
  95. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/config.pyi +0 -0
  96. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/crypt.py +0 -0
  97. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/crypt.pyi +0 -0
  98. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/db.py +0 -0
  99. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/db.pyi +0 -0
  100. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/event.py +0 -0
  101. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/event.pyi +0 -0
  102. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/hash.py +0 -0
  103. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/hash.pyi +0 -0
  104. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/log.py +0 -0
  105. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/facade/log.pyi +0 -0
  106. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/py.typed +0 -0
  107. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/results.py +0 -0
  108. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/strconv.py +0 -0
  109. {python_neva-3.6.0 → python_neva-3.7.0}/neva/support/time.py +0 -0
  110. {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/__init__.py +0 -0
  111. {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/fakes.py +0 -0
  112. {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/fixtures.py +0 -0
  113. {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/py.typed +0 -0
  114. {python_neva-3.6.0 → python_neva-3.7.0}/neva/testing/test_case.py +0 -0
  115. {python_neva-3.6.0 → python_neva-3.7.0}/scripts/retag-with-changelog.sh +0 -0
  116. {python_neva-3.6.0 → python_neva-3.7.0}/tests/__init__.py +0 -0
  117. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/__init__.py +0 -0
  118. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_cache.py +0 -0
  119. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_context.py +0 -0
  120. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_extends.py +0 -0
  121. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_lifetimes.py +0 -0
  122. {python_neva-3.6.0 → python_neva-3.7.0}/tests/arch/test_scope.py +0 -0
  123. {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/__init__.py +0 -0
  124. {python_neva-3.6.0 → python_neva-3.7.0}/tests/config/test_repository.py +0 -0
  125. {python_neva-3.6.0 → python_neva-3.7.0}/tests/conftest.py +0 -0
  126. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/__init__.py +0 -0
  127. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_connection_manager.py +0 -0
  128. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_database_manager.py +0 -0
  129. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_detached_lifespan.py +0 -0
  130. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_edge_cases.py +0 -0
  131. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_multi_connection.py +0 -0
  132. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_sqlalchemy_integration.py +0 -0
  133. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction.py +0 -0
  134. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction_context.py +0 -0
  135. {python_neva-3.6.0 → python_neva-3.7.0}/tests/database/test_transaction_registry.py +0 -0
  136. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/__init__.py +0 -0
  137. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_before_dispatch.py +0 -0
  138. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_binding.py +0 -0
  139. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_deferred.py +0 -0
  140. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_dispatch.py +0 -0
  141. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_event.py +0 -0
  142. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_function_listener.py +0 -0
  143. {python_neva-3.6.0 → python_neva-3.7.0}/tests/events/test_immediate.py +0 -0
  144. {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/__init__.py +0 -0
  145. {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/test_correlation.py +0 -0
  146. {python_neva-3.6.0 → python_neva-3.7.0}/tests/obs/test_profiler.py +0 -0
  147. {python_neva-3.6.0 → python_neva-3.7.0}/tests/security/__init__.py +0 -0
  148. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/__init__.py +0 -0
  149. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_event_fake.py +0 -0
  150. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_facade_restore.py +0 -0
  151. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_fixtures.py +0 -0
  152. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_refresh_database.py +0 -0
  153. {python_neva-3.6.0 → python_neva-3.7.0}/tests/testing/test_test_case.py +0 -0
  154. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/KNOWN-DISCREPANCIES.md +0 -0
  155. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/configuration.md +0 -0
  156. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/database-and-transactions.md +0 -0
  157. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/dependency-injection.md +0 -0
  158. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/events.md +0 -0
  159. {python_neva-3.6.0 → python_neva-3.7.0}/wiki/architecture/result-option.md +0 -0
  160. {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
@@ -16,7 +16,7 @@ repos:
16
16
  - id: debug-statements
17
17
 
18
18
  - repo: https://github.com/astral-sh/ruff-pre-commit
19
- rev: v0.15.6
19
+ rev: v0.16.5
20
20
  hooks:
21
21
  - id: ruff
22
22
  args: [--fix, --exit-non-zero-on-fix]
@@ -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.6.0
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: typer>=0.21.1
17
+ Requires-Dist: typing-extensions>=4.13
18
18
  Provides-Extra: fastapi
19
- Requires-Dist: neva-fastapi>=1.0.0; extra == 'fastapi'
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'
@@ -100,7 +100,7 @@ class ConnectionManager:
100
100
 
101
101
  @property
102
102
  def engine(self) -> asyncio.AsyncEngine:
103
- """Returns the underlying engine."""
103
+ """The underlying engine."""
104
104
  return self._engine
105
105
 
106
106
  async def dispose(self) -> None:
@@ -134,7 +134,7 @@ class BoundTransaction(Transaction):
134
134
 
135
135
  @property
136
136
  def is_accessible_from_current_task(self) -> bool:
137
- """Return True if the current asyncio task may use this transaction's session.
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
- """Return the created drivers."""
77
+ """The created drivers."""
78
78
  return self._strategies
79
79
 
80
80
  @property
81
81
  def app(self) -> Application:
82
- """Return the underlying container."""
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.6.0"
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.0.0"]
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)) # noqa: S311
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)) # noqa: S311
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() # noqa: FBT003
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" # noqa: S105
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" # noqa: S105
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" # noqa: S105
70
- wrong_password = "wrong_password" # noqa: S105
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" # noqa: S105
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" # noqa: S105
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" # noqa: S105
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" # noqa: S105
171
+ password = "secret_password" # ruff: ignore[hardcoded-password-string]
172
172
 
173
173
  hashed = await Hash.make_async(password)
174
174