python-neva 2.1.0__tar.gz → 2.2.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 (121) hide show
  1. {python_neva-2.1.0 → python_neva-2.2.0}/.pre-commit-config.yaml +5 -8
  2. {python_neva-2.1.0 → python_neva-2.2.0}/PKG-INFO +1 -1
  3. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/app.py +3 -3
  4. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/application.py +25 -31
  5. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/facade.py +8 -4
  6. {python_neva-2.1.0 → python_neva-2.2.0}/neva/config/__init__.py +0 -2
  7. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/encryption/encrypter.py +3 -1
  8. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/results.py +4 -4
  9. {python_neva-2.1.0 → python_neva-2.2.0}/neva/testing/fixtures.py +2 -3
  10. {python_neva-2.1.0 → python_neva-2.2.0}/pyproject.toml +8 -6
  11. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/test_refresh_database.py +26 -0
  12. {python_neva-2.1.0 → python_neva-2.2.0}/uv.lock +136 -51
  13. python_neva-2.1.0/neva/config/provider.py +0 -74
  14. {python_neva-2.1.0 → python_neva-2.2.0}/.envrc +0 -0
  15. {python_neva-2.1.0 → python_neva-2.2.0}/.gitignore +0 -0
  16. {python_neva-2.1.0 → python_neva-2.2.0}/.python-version +0 -0
  17. {python_neva-2.1.0 → python_neva-2.2.0}/README.md +0 -0
  18. {python_neva-2.1.0 → python_neva-2.2.0}/neva/__init__.py +0 -0
  19. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/__init__.py +0 -0
  20. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/config.py +0 -0
  21. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/faststream.py +0 -0
  22. {python_neva-2.1.0 → python_neva-2.2.0}/neva/arch/service_provider.py +0 -0
  23. {python_neva-2.1.0 → python_neva-2.2.0}/neva/config/base_providers.py +0 -0
  24. {python_neva-2.1.0 → python_neva-2.2.0}/neva/config/loader.py +0 -0
  25. {python_neva-2.1.0 → python_neva-2.2.0}/neva/config/repository.py +0 -0
  26. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/__init__.py +0 -0
  27. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/config.py +0 -0
  28. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/connection.py +0 -0
  29. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/manager.py +0 -0
  30. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/provider.py +0 -0
  31. {python_neva-2.1.0 → python_neva-2.2.0}/neva/database/transaction.py +0 -0
  32. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/__init__.py +0 -0
  33. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/dispatcher.py +0 -0
  34. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/event.py +0 -0
  35. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/event_registry.py +0 -0
  36. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/listener.py +0 -0
  37. {python_neva-2.1.0 → python_neva-2.2.0}/neva/events/provider.py +0 -0
  38. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/__init__.py +0 -0
  39. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/logging/__init__.py +0 -0
  40. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/logging/manager.py +0 -0
  41. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/logging/provider.py +0 -0
  42. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/middleware/__init__.py +0 -0
  43. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/middleware/correlation.py +0 -0
  44. {python_neva-2.1.0 → python_neva-2.2.0}/neva/obs/middleware/profiler.py +0 -0
  45. {python_neva-2.1.0 → python_neva-2.2.0}/neva/py.typed +0 -0
  46. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/__init__.py +0 -0
  47. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/encryption/__init__.py +0 -0
  48. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/encryption/protocol.py +0 -0
  49. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/__init__.py +0 -0
  50. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/config.py +0 -0
  51. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/hash_manager.py +0 -0
  52. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/hashers/__init__.py +0 -0
  53. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/hashers/argon2.py +0 -0
  54. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/hashers/bcrypt.py +0 -0
  55. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/hashing/hashers/protocol.py +0 -0
  56. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/provider.py +0 -0
  57. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/tokens/__init__.py +0 -0
  58. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/tokens/generate_token.py +0 -0
  59. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/tokens/hash_token.py +0 -0
  60. {python_neva-2.1.0 → python_neva-2.2.0}/neva/security/tokens/verify_token.py +0 -0
  61. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/__init__.py +0 -0
  62. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/accessors.py +0 -0
  63. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/__init__.py +0 -0
  64. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/app.py +0 -0
  65. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/app.pyi +0 -0
  66. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/config.py +0 -0
  67. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/config.pyi +0 -0
  68. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/crypt.py +0 -0
  69. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/crypt.pyi +0 -0
  70. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/db.py +0 -0
  71. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/db.pyi +0 -0
  72. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/event.py +0 -0
  73. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/event.pyi +0 -0
  74. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/hash.py +0 -0
  75. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/hash.pyi +0 -0
  76. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/log.py +0 -0
  77. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/facade/log.pyi +0 -0
  78. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/strategy.py +0 -0
  79. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/strconv.py +0 -0
  80. {python_neva-2.1.0 → python_neva-2.2.0}/neva/support/time.py +0 -0
  81. {python_neva-2.1.0 → python_neva-2.2.0}/neva/testing/__init__.py +0 -0
  82. {python_neva-2.1.0 → python_neva-2.2.0}/neva/testing/fakes.py +0 -0
  83. {python_neva-2.1.0 → python_neva-2.2.0}/neva/testing/http.py +0 -0
  84. {python_neva-2.1.0 → python_neva-2.2.0}/neva/testing/test_case.py +0 -0
  85. {python_neva-2.1.0 → python_neva-2.2.0}/ruff.toml +0 -0
  86. {python_neva-2.1.0 → python_neva-2.2.0}/tests/__init__.py +0 -0
  87. {python_neva-2.1.0 → python_neva-2.2.0}/tests/arch/__init__.py +0 -0
  88. {python_neva-2.1.0 → python_neva-2.2.0}/tests/arch/test_scope.py +0 -0
  89. {python_neva-2.1.0 → python_neva-2.2.0}/tests/config/__init__.py +0 -0
  90. {python_neva-2.1.0 → python_neva-2.2.0}/tests/config/test_loader.py +0 -0
  91. {python_neva-2.1.0 → python_neva-2.2.0}/tests/config/test_repository.py +0 -0
  92. {python_neva-2.1.0 → python_neva-2.2.0}/tests/conftest.py +0 -0
  93. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/__init__.py +0 -0
  94. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/conftest.py +0 -0
  95. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_connection_manager.py +0 -0
  96. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_database_manager.py +0 -0
  97. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_edge_cases.py +0 -0
  98. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_multi_connection.py +0 -0
  99. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_sqlalchemy_integration.py +0 -0
  100. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_transaction.py +0 -0
  101. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_transaction_context.py +0 -0
  102. {python_neva-2.1.0 → python_neva-2.2.0}/tests/database/test_transaction_registry.py +0 -0
  103. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/__init__.py +0 -0
  104. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/conftest.py +0 -0
  105. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_deferred.py +0 -0
  106. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_dispatch.py +0 -0
  107. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_event.py +0 -0
  108. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_event_registry.py +0 -0
  109. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_function_listener.py +0 -0
  110. {python_neva-2.1.0 → python_neva-2.2.0}/tests/events/test_immediate.py +0 -0
  111. {python_neva-2.1.0 → python_neva-2.2.0}/tests/obs/__init__.py +0 -0
  112. {python_neva-2.1.0 → python_neva-2.2.0}/tests/obs/test_correlation.py +0 -0
  113. {python_neva-2.1.0 → python_neva-2.2.0}/tests/obs/test_profiler.py +0 -0
  114. {python_neva-2.1.0 → python_neva-2.2.0}/tests/security/__init__.py +0 -0
  115. {python_neva-2.1.0 → python_neva-2.2.0}/tests/security/test_encrypter.py +0 -0
  116. {python_neva-2.1.0 → python_neva-2.2.0}/tests/security/test_hash_manager.py +0 -0
  117. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/__init__.py +0 -0
  118. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/test_event_fake.py +0 -0
  119. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/test_facade_restore.py +0 -0
  120. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/test_fixtures.py +0 -0
  121. {python_neva-2.1.0 → python_neva-2.2.0}/tests/testing/test_test_case.py +0 -0
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/gitleaks/gitleaks
3
- rev: v8.24.2
3
+ rev: v8.30.1
4
4
  hooks:
5
5
  - id: gitleaks
6
6
 
@@ -16,16 +16,13 @@ repos:
16
16
  - id: debug-statements
17
17
 
18
18
  - repo: https://github.com/astral-sh/ruff-pre-commit
19
- rev: v0.13.3
19
+ rev: v0.15.6
20
20
  hooks:
21
21
  - id: ruff
22
22
  args: [--fix, --exit-non-zero-on-fix]
23
23
  - id: ruff-format
24
24
 
25
- - repo: local
25
+ - repo: https://github.com/pre-commit/mirrors-mypy
26
+ rev: v1.19.1
26
27
  hooks:
27
- - id: ty
28
- name: ty check
29
- entry: ty check . --project neva
30
- files: ^neva/
31
- language: system
28
+ - id: mypy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-neva
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: aiosqlite>=0.20.0
@@ -13,6 +13,7 @@ from typing import Any, Callable
13
13
  import dishka
14
14
  import fastapi
15
15
  from dishka.integrations.fastapi import setup_dishka
16
+ from starlette.middleware import Middleware
16
17
  from starlette.routing import BaseRoute
17
18
  from starlette.types import StatefulLifespan, StatelessLifespan
18
19
 
@@ -28,7 +29,7 @@ class App(fastapi.FastAPI):
28
29
  self,
29
30
  *,
30
31
  routes: list[BaseRoute] | None = None,
31
- middlewares: Sequence[type] | None = None,
32
+ middlewares: Sequence[Middleware] | None = None,
32
33
  lifespan: StatelessLifespan["App"] | StatefulLifespan["App"] | None = None,
33
34
  config_path: str | Path | None = None,
34
35
  ) -> None:
@@ -58,9 +59,8 @@ class App(fastapi.FastAPI):
58
59
  docs_url=config.get("app.docs_url", default="/docs").unwrap(),
59
60
  redoc_url=config.get("app.redoc_url", default="/redoc").unwrap(),
60
61
  lifespan=self._create_lifespan(),
62
+ middleware=middlewares,
61
63
  )
62
- for middleware in middlewares or []:
63
- self.add_middleware(middleware)
64
64
 
65
65
  setup_dishka(self.application.container, app=self)
66
66
 
@@ -12,6 +12,7 @@ from dishka.integrations.fastapi import FastapiProvider
12
12
  from neva import Err, Ok, Result
13
13
  from neva.arch.facade import Facade
14
14
  from neva.arch.service_provider import Bootable, ServiceProvider
15
+ from neva.config.loader import ConfigLoader
15
16
 
16
17
 
17
18
  class Application:
@@ -28,45 +29,38 @@ class Application:
28
29
  RuntimeError: If the application fails to initialize.
29
30
  """
30
31
  from neva.config.base_providers import base_providers
31
- from neva.config.provider import ConfigServiceProvider
32
32
  from neva.config.repository import ConfigRepository
33
33
 
34
+ self.config: ConfigRepository = ConfigRepository()
34
35
  self.providers: dict[type, ServiceProvider] = {}
35
36
  self.di_provider: dishka.Provider = dishka.Provider(scope=dishka.Scope.APP)
36
37
 
37
- configuration_path = config_path or os.getenv("NEVA_CONFIG_PATH", default=None)
38
- config_provider = ConfigServiceProvider(
39
- app=self,
40
- config_path=configuration_path,
41
- ).register()
42
- if config_provider.is_err:
43
- raise RuntimeError(
44
- f"Failed to register config provider: {config_provider.unwrap_err()}"
45
- )
46
- self.providers[ConfigServiceProvider] = config_provider.unwrap()
47
-
48
- self.register_providers(base_providers())
49
- _ = self.di_provider.provide(source=lambda: self, provides=Application)
50
-
51
- self.container: dishka.Container = dishka.make_container(self.di_provider)
52
-
53
- config_result: Result[ConfigRepository, str] = self.make(
54
- interface=ConfigRepository
38
+ configuration_path = (
39
+ config_path
40
+ if config_path is not None
41
+ else os.getenv("NEVA_CONFIG_PATH", default=Path.cwd())
55
42
  )
56
- match config_result:
57
- case Ok(config):
58
- self.config: ConfigRepository = config
59
- providers_from_file = config.get("providers.providers").unwrap_or([])
60
- providers_from_app = config.get("app.providers").unwrap_or([])
61
- providers: set[type[ServiceProvider]] = set(providers_from_file).union(
62
- set(providers_from_app)
63
- )
64
- _ = self.register_providers(providers)
43
+ match ConfigLoader(configuration_path).load_all():
65
44
  case Err(e):
66
- raise RuntimeError(f"Failed to load configuration during boot: {e}")
67
-
45
+ raise RuntimeError(f"Failed to register config: {e}")
46
+ case Ok(configs):
47
+ if any(
48
+ self.config.merge(namespace, config).is_err
49
+ for namespace, config in configs.items()
50
+ ):
51
+ raise RuntimeError("Failed to register config")
52
+
53
+ self.bind(lambda: self.config, interface=ConfigRepository)
54
+
55
+ providers_from_file = self.config.get("providers.providers").unwrap_or([])
56
+ providers_from_app = self.config.get("app.providers").unwrap_or([])
57
+ providers: set[type[ServiceProvider]] = set(providers_from_file).union(
58
+ set(providers_from_app)
59
+ )
60
+ self.register_providers(base_providers().union(providers))
61
+ self.bind(source=lambda: self, interface=Application)
68
62
  self._bind_event_listeners()
69
- self.container = dishka.make_container(self.di_provider)
63
+ self.container: dishka.Container = dishka.make_container(self.di_provider)
70
64
 
71
65
  def bind_to_fastapi(self) -> None:
72
66
  """Setup the FastapiProvider for FastAPI integration."""
@@ -8,7 +8,7 @@ injection container, enabling convenient access without explicit dependency inje
8
8
  from abc import ABC, ABCMeta, abstractmethod
9
9
  from collections.abc import Iterator
10
10
  from contextlib import contextmanager
11
- from typing import TYPE_CHECKING, Any, ClassVar
11
+ from typing import TYPE_CHECKING, Any, ClassVar, cast
12
12
  from unittest.mock import AsyncMock, MagicMock
13
13
 
14
14
  from neva import Ok, Option, Result, from_optional
@@ -150,8 +150,12 @@ class FacadeMeta(ABCMeta):
150
150
  The MagicMock spy installed as the facade root.
151
151
 
152
152
  """
153
- app = cls._get_app().ok_or(f"Cannot spy on {cls.__name__}: no application set.")
154
- real = app.and_then(lambda a: a.make(cls.get_facade_accessor())).unwrap()
153
+ real: object = (
154
+ cls._get_app()
155
+ .ok_or(f"Cannot spy on {cls.__name__}: no application set.")
156
+ .and_then(lambda a: a.make(cls.get_facade_accessor()))
157
+ .unwrap()
158
+ )
155
159
  spy_obj = MagicMock(spec=type(real), wraps=real)
156
160
  cls.swap(spy_obj)
157
161
  return spy_obj
@@ -193,7 +197,7 @@ class FacadeMeta(ABCMeta):
193
197
 
194
198
  """
195
199
  if cls in FacadeMeta._fake_instances:
196
- return Ok(FacadeMeta._fake_instances[cls])
200
+ return Ok(cast(T, FacadeMeta._fake_instances[cls]))
197
201
  return app.make(interface)
198
202
 
199
203
 
@@ -1,10 +1,8 @@
1
1
  """Configuration system."""
2
2
 
3
- from neva.config.provider import ConfigServiceProvider
4
3
  from neva.config.repository import ConfigRepository
5
4
 
6
5
 
7
6
  __all__ = [
8
7
  "ConfigRepository",
9
- "ConfigServiceProvider",
10
8
  ]
@@ -51,7 +51,9 @@ class AesEncrypter:
51
51
  Returns:
52
52
  Ok with base64-encoded encrypted payload, or Err with message.
53
53
  """
54
- wrapper = {"__str__": value} if isinstance(value, str) else {"__json__": value}
54
+ wrapper: dict[str, JsonValue] = (
55
+ {"__str__": value} if isinstance(value, str) else {"__json__": value}
56
+ )
55
57
 
56
58
  try:
57
59
  payload = json.dumps(wrapper)
@@ -246,7 +246,7 @@ class OptionProtocol[T](ABC):
246
246
 
247
247
 
248
248
  @dataclass(eq=True, frozen=True)
249
- class Some(OptionProtocol[T]):
249
+ class Some[T](OptionProtocol[T]):
250
250
  """An Option containing a value.
251
251
 
252
252
  Represents the presence of a value in an Option type. All transformation
@@ -330,7 +330,7 @@ class Some(OptionProtocol[T]):
330
330
 
331
331
 
332
332
  @dataclass(eq=True, frozen=True)
333
- class Nothing(OptionProtocol[T]):
333
+ class Nothing[T](OptionProtocol[T]):
334
334
  """An Option containing no value.
335
335
 
336
336
  Represents the absence of a value in an Option type. All transformation
@@ -594,7 +594,7 @@ class ResultProtocol[T, E](ABC):
594
594
 
595
595
 
596
596
  @dataclass(eq=True, frozen=True)
597
- class Ok(ResultProtocol[T, E]):
597
+ class Ok[T, E](ResultProtocol[T, E]):
598
598
  """A Result containing a success value.
599
599
 
600
600
  Represents a successful operation in a Result type. All transformation
@@ -671,7 +671,7 @@ class Ok(ResultProtocol[T, E]):
671
671
 
672
672
 
673
673
  @dataclass(eq=True, frozen=True)
674
- class Err(ResultProtocol[T, E]):
674
+ class Err[T, E](ResultProtocol[T, E]):
675
675
  """A Result containing an error value.
676
676
 
677
677
  Represents a failed operation in a Result type. All transformation methods
@@ -1,9 +1,8 @@
1
1
  """Fixtures for testing."""
2
2
 
3
3
  from collections.abc import AsyncIterator, Callable
4
- from contextlib import asynccontextmanager
4
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager
5
5
  from pathlib import Path
6
- from typing import AsyncContextManager
7
6
 
8
7
  import pytest
9
8
 
@@ -45,7 +44,7 @@ def webapp(test_config: Path) -> App:
45
44
 
46
45
 
47
46
  @pytest.fixture
48
- def app_factory() -> Callable[[Path], AsyncContextManager[Application]]:
47
+ def app_factory() -> Callable[[Path], AbstractAsyncContextManager[Application]]:
49
48
  """Factory fixture for creating applications with custom configs.
50
49
 
51
50
  Returns:
@@ -7,7 +7,7 @@ packages = ["neva"]
7
7
 
8
8
  [project]
9
9
  name = "python-neva"
10
- version = "2.1.0"
10
+ version = "2.2.0"
11
11
  description = "Add your description here"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.12"
@@ -31,6 +31,7 @@ testing = ["pytest>=9.0.2", "pytest-asyncio>=0.25.3"]
31
31
  [dependency-groups]
32
32
  dev = [
33
33
  "bandit>=1.9.2",
34
+ "mypy>=1.19.1",
34
35
  "poethepoet>=0.38.0",
35
36
  "polyfactory>=3.1.0",
36
37
  "pre-commit>=4.5.0",
@@ -38,8 +39,7 @@ dev = [
38
39
  "pytest-asyncio>=0.25.3",
39
40
  "pytest-benchmark>=5.2.3",
40
41
  "pytest-cov>=7.0.0",
41
- "ruff>=0.14.8",
42
- "ty>=0.0.8",
42
+ "ruff>=0.15.6",
43
43
  ]
44
44
 
45
45
  [tool.pytest.ini_options]
@@ -49,9 +49,11 @@ testpaths = ["tests"]
49
49
 
50
50
  [tool.poe.tasks]
51
51
  # Code quality
52
- lint = "ruff check"
53
- fmt = "ruff format"
54
- tc = "ty check"
52
+ ruff = "uv run ruff"
53
+ mypy = "uv run mypy"
54
+ lint = "poe ruff check"
55
+ fmt = "poe ruff format"
56
+ tc = "poe mypy ."
55
57
 
56
58
  # Testing
57
59
  test = "pytest"
@@ -83,3 +83,29 @@ class TestRefreshDatabaseMechanism(DatabaseTestCase):
83
83
 
84
84
  result = await session.execute(select(Item).where(Item.id == 10))
85
85
  assert result.scalar_one().name == "Visible"
86
+
87
+
88
+ class TestRefreshDatabaseNestedRollback(DatabaseTestCase):
89
+ """Verify that data written inside a nested db.begin() is rolled back.
90
+
91
+ A nested begin() releases a savepoint on exit, folding its writes into
92
+ the outer connection-level transaction. The outer RefreshDatabase rollback
93
+ must undo those writes too. Tests use an a/b prefix to enforce order.
94
+ """
95
+
96
+ async def test_a_write_inside_nested_begin(self) -> None:
97
+ """Write via a nested db.begin() that exits normally (savepoint released)."""
98
+ db = self.app.make(DatabaseManager).unwrap()
99
+ async with db.begin() as tx:
100
+ tx.session.add(Item(id=50, name="Nested"))
101
+ await tx.session.flush()
102
+
103
+ result = await tx.session.execute(select(Item).where(Item.id == 50))
104
+ assert result.scalar_one().name == "Nested"
105
+
106
+ async def test_b_nested_write_is_gone_after_rollback(self) -> None:
107
+ """The savepoint-released write must not survive across tests."""
108
+ db = self.app.make(DatabaseManager).unwrap()
109
+ async with db.begin() as tx:
110
+ result = await tx.session.execute(select(Item).where(Item.id == 50))
111
+ assert result.scalar_one_or_none() is None
@@ -825,6 +825,66 @@ wheels = [
825
825
  { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
826
826
  ]
827
827
 
828
+ [[package]]
829
+ name = "librt"
830
+ version = "0.8.1"
831
+ source = { registry = "https://pypi.org/simple" }
832
+ sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" }
833
+ wheels = [
834
+ { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" },
835
+ { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" },
836
+ { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" },
837
+ { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" },
838
+ { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" },
839
+ { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" },
840
+ { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" },
841
+ { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" },
842
+ { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" },
843
+ { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" },
844
+ { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" },
845
+ { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" },
846
+ { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" },
847
+ { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" },
848
+ { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" },
849
+ { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" },
850
+ { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" },
851
+ { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" },
852
+ { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" },
853
+ { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" },
854
+ { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" },
855
+ { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" },
856
+ { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" },
857
+ { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" },
858
+ { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" },
859
+ { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" },
860
+ { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" },
861
+ { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" },
862
+ { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" },
863
+ { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" },
864
+ { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" },
865
+ { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" },
866
+ { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" },
867
+ { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" },
868
+ { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" },
869
+ { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" },
870
+ { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" },
871
+ { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" },
872
+ { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" },
873
+ { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" },
874
+ { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" },
875
+ { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" },
876
+ { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" },
877
+ { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" },
878
+ { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" },
879
+ { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" },
880
+ { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" },
881
+ { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" },
882
+ { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" },
883
+ { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" },
884
+ { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" },
885
+ { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" },
886
+ ]
887
+
828
888
  [[package]]
829
889
  name = "markdown-it-py"
830
890
  version = "4.0.0"
@@ -909,6 +969,48 @@ wheels = [
909
969
  { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
910
970
  ]
911
971
 
972
+ [[package]]
973
+ name = "mypy"
974
+ version = "1.19.1"
975
+ source = { registry = "https://pypi.org/simple" }
976
+ dependencies = [
977
+ { name = "librt", marker = "platform_python_implementation != 'PyPy'" },
978
+ { name = "mypy-extensions" },
979
+ { name = "pathspec" },
980
+ { name = "typing-extensions" },
981
+ ]
982
+ sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" }
983
+ wheels = [
984
+ { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" },
985
+ { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" },
986
+ { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" },
987
+ { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" },
988
+ { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" },
989
+ { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" },
990
+ { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" },
991
+ { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" },
992
+ { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" },
993
+ { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" },
994
+ { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" },
995
+ { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" },
996
+ { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" },
997
+ { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" },
998
+ { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" },
999
+ { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" },
1000
+ { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" },
1001
+ { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" },
1002
+ { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" },
1003
+ ]
1004
+
1005
+ [[package]]
1006
+ name = "mypy-extensions"
1007
+ version = "1.1.0"
1008
+ source = { registry = "https://pypi.org/simple" }
1009
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
1010
+ wheels = [
1011
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
1012
+ ]
1013
+
912
1014
  [[package]]
913
1015
  name = "nodeenv"
914
1016
  version = "1.10.0"
@@ -989,6 +1091,15 @@ wheels = [
989
1091
  { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" },
990
1092
  ]
991
1093
 
1094
+ [[package]]
1095
+ name = "pathspec"
1096
+ version = "1.0.4"
1097
+ source = { registry = "https://pypi.org/simple" }
1098
+ sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" }
1099
+ wheels = [
1100
+ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
1101
+ ]
1102
+
992
1103
  [[package]]
993
1104
  name = "platformdirs"
994
1105
  version = "4.5.1"
@@ -1327,7 +1438,7 @@ wheels = [
1327
1438
 
1328
1439
  [[package]]
1329
1440
  name = "python-neva"
1330
- version = "2.1.0"
1441
+ version = "2.2.0"
1331
1442
  source = { editable = "." }
1332
1443
  dependencies = [
1333
1444
  { name = "aiosqlite" },
@@ -1352,6 +1463,7 @@ testing = [
1352
1463
  [package.dev-dependencies]
1353
1464
  dev = [
1354
1465
  { name = "bandit" },
1466
+ { name = "mypy" },
1355
1467
  { name = "poethepoet" },
1356
1468
  { name = "polyfactory" },
1357
1469
  { name = "pre-commit" },
@@ -1360,7 +1472,6 @@ dev = [
1360
1472
  { name = "pytest-benchmark" },
1361
1473
  { name = "pytest-cov" },
1362
1474
  { name = "ruff" },
1363
- { name = "ty" },
1364
1475
  ]
1365
1476
 
1366
1477
  [package.metadata]
@@ -1384,6 +1495,7 @@ provides-extras = ["testing"]
1384
1495
  [package.metadata.requires-dev]
1385
1496
  dev = [
1386
1497
  { name = "bandit", specifier = ">=1.9.2" },
1498
+ { name = "mypy", specifier = ">=1.19.1" },
1387
1499
  { name = "poethepoet", specifier = ">=0.38.0" },
1388
1500
  { name = "polyfactory", specifier = ">=3.1.0" },
1389
1501
  { name = "pre-commit", specifier = ">=4.5.0" },
@@ -1391,8 +1503,7 @@ dev = [
1391
1503
  { name = "pytest-asyncio", specifier = ">=0.25.3" },
1392
1504
  { name = "pytest-benchmark", specifier = ">=5.2.3" },
1393
1505
  { name = "pytest-cov", specifier = ">=7.0.0" },
1394
- { name = "ruff", specifier = ">=0.14.8" },
1395
- { name = "ty", specifier = ">=0.0.8" },
1506
+ { name = "ruff", specifier = ">=0.15.6" },
1396
1507
  ]
1397
1508
 
1398
1509
  [[package]]
@@ -1538,28 +1649,27 @@ wheels = [
1538
1649
 
1539
1650
  [[package]]
1540
1651
  name = "ruff"
1541
- version = "0.14.13"
1542
- source = { registry = "https://pypi.org/simple" }
1543
- sdist = { url = "https://files.pythonhosted.org/packages/50/0a/1914efb7903174b381ee2ffeebb4253e729de57f114e63595114c8ca451f/ruff-0.14.13.tar.gz", hash = "sha256:83cd6c0763190784b99650a20fec7633c59f6ebe41c5cc9d45ee42749563ad47", size = 6059504, upload-time = "2026-01-15T20:15:16.918Z" }
1544
- wheels = [
1545
- { url = "https://files.pythonhosted.org/packages/c3/ae/0deefbc65ca74b0ab1fd3917f94dc3b398233346a74b8bbb0a916a1a6bf6/ruff-0.14.13-py3-none-linux_armv6l.whl", hash = "sha256:76f62c62cd37c276cb03a275b198c7c15bd1d60c989f944db08a8c1c2dbec18b", size = 13062418, upload-time = "2026-01-15T20:14:50.779Z" },
1546
- { url = "https://files.pythonhosted.org/packages/47/df/5916604faa530a97a3c154c62a81cb6b735c0cb05d1e26d5ad0f0c8ac48a/ruff-0.14.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:914a8023ece0528d5cc33f5a684f5f38199bbb566a04815c2c211d8f40b5d0ed", size = 13442344, upload-time = "2026-01-15T20:15:07.94Z" },
1547
- { url = "https://files.pythonhosted.org/packages/4c/f3/e0e694dd69163c3a1671e102aa574a50357536f18a33375050334d5cd517/ruff-0.14.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d24899478c35ebfa730597a4a775d430ad0d5631b8647a3ab368c29b7e7bd063", size = 12354720, upload-time = "2026-01-15T20:15:09.854Z" },
1548
- { url = "https://files.pythonhosted.org/packages/c3/e8/67f5fcbbaee25e8fc3b56cc33e9892eca7ffe09f773c8e5907757a7e3bdb/ruff-0.14.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9aaf3870f14d925bbaf18b8a2347ee0ae7d95a2e490e4d4aea6813ed15ebc80e", size = 12774493, upload-time = "2026-01-15T20:15:20.908Z" },
1549
- { url = "https://files.pythonhosted.org/packages/6b/ce/d2e9cb510870b52a9565d885c0d7668cc050e30fa2c8ac3fb1fda15c083d/ruff-0.14.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac5b7f63dd3b27cc811850f5ffd8fff845b00ad70e60b043aabf8d6ecc304e09", size = 12815174, upload-time = "2026-01-15T20:15:05.74Z" },
1550
- { url = "https://files.pythonhosted.org/packages/88/00/c38e5da58beebcf4fa32d0ddd993b63dfacefd02ab7922614231330845bf/ruff-0.14.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d2b1097750d90ba82ce4ba676e85230a0ed694178ca5e61aa9b459970b3eb9", size = 13680909, upload-time = "2026-01-15T20:15:14.537Z" },
1551
- { url = "https://files.pythonhosted.org/packages/61/61/cd37c9dd5bd0a3099ba79b2a5899ad417d8f3b04038810b0501a80814fd7/ruff-0.14.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d0bf87705acbbcb8d4c24b2d77fbb73d40210a95c3903b443cd9e30824a5032", size = 15144215, upload-time = "2026-01-15T20:15:22.886Z" },
1552
- { url = "https://files.pythonhosted.org/packages/56/8a/85502d7edbf98c2df7b8876f316c0157359165e16cdf98507c65c8d07d3d/ruff-0.14.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3eb5da8e2c9e9f13431032fdcbe7681de9ceda5835efee3269417c13f1fed5c", size = 14706067, upload-time = "2026-01-15T20:14:48.271Z" },
1553
- { url = "https://files.pythonhosted.org/packages/7e/2f/de0df127feb2ee8c1e54354dc1179b4a23798f0866019528c938ba439aca/ruff-0.14.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:642442b42957093811cd8d2140dfadd19c7417030a7a68cf8d51fcdd5f217427", size = 14133916, upload-time = "2026-01-15T20:14:57.357Z" },
1554
- { url = "https://files.pythonhosted.org/packages/0d/77/9b99686bb9fe07a757c82f6f95e555c7a47801a9305576a9c67e0a31d280/ruff-0.14.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4acdf009f32b46f6e8864af19cbf6841eaaed8638e65c8dac845aea0d703c841", size = 13859207, upload-time = "2026-01-15T20:14:55.111Z" },
1555
- { url = "https://files.pythonhosted.org/packages/7d/46/2bdcb34a87a179a4d23022d818c1c236cb40e477faf0d7c9afb6813e5876/ruff-0.14.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:591a7f68860ea4e003917d19b5c4f5ac39ff558f162dc753a2c5de897fd5502c", size = 14043686, upload-time = "2026-01-15T20:14:52.841Z" },
1556
- { url = "https://files.pythonhosted.org/packages/1a/a9/5c6a4f56a0512c691cf143371bcf60505ed0f0860f24a85da8bd123b2bf1/ruff-0.14.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:774c77e841cc6e046fc3e91623ce0903d1cd07e3a36b1a9fe79b81dab3de506b", size = 12663837, upload-time = "2026-01-15T20:15:18.921Z" },
1557
- { url = "https://files.pythonhosted.org/packages/fe/bb/b920016ece7651fa7fcd335d9d199306665486694d4361547ccb19394c44/ruff-0.14.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:61f4e40077a1248436772bb6512db5fc4457fe4c49e7a94ea7c5088655dd21ae", size = 12805867, upload-time = "2026-01-15T20:14:59.272Z" },
1558
- { url = "https://files.pythonhosted.org/packages/7d/b3/0bd909851e5696cd21e32a8fc25727e5f58f1934b3596975503e6e85415c/ruff-0.14.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6d02f1428357fae9e98ac7aa94b7e966fd24151088510d32cf6f902d6c09235e", size = 13208528, upload-time = "2026-01-15T20:15:03.732Z" },
1559
- { url = "https://files.pythonhosted.org/packages/3b/3b/e2d94cb613f6bbd5155a75cbe072813756363eba46a3f2177a1fcd0cd670/ruff-0.14.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e399341472ce15237be0c0ae5fbceca4b04cd9bebab1a2b2c979e015455d8f0c", size = 13929242, upload-time = "2026-01-15T20:15:11.918Z" },
1560
- { url = "https://files.pythonhosted.org/packages/6a/c5/abd840d4132fd51a12f594934af5eba1d5d27298a6f5b5d6c3be45301caf/ruff-0.14.13-py3-none-win32.whl", hash = "sha256:ef720f529aec113968b45dfdb838ac8934e519711da53a0456038a0efecbd680", size = 12919024, upload-time = "2026-01-15T20:14:43.647Z" },
1561
- { url = "https://files.pythonhosted.org/packages/c2/55/6384b0b8ce731b6e2ade2b5449bf07c0e4c31e8a2e68ea65b3bafadcecc5/ruff-0.14.13-py3-none-win_amd64.whl", hash = "sha256:6070bd026e409734b9257e03e3ef18c6e1a216f0435c6751d7a8ec69cb59abef", size = 14097887, upload-time = "2026-01-15T20:15:01.48Z" },
1562
- { url = "https://files.pythonhosted.org/packages/4d/e1/7348090988095e4e39560cfc2f7555b1b2a7357deba19167b600fdf5215d/ruff-0.14.13-py3-none-win_arm64.whl", hash = "sha256:7ab819e14f1ad9fe39f246cfcc435880ef7a9390d81a2b6ac7e01039083dd247", size = 13080224, upload-time = "2026-01-15T20:14:45.853Z" },
1652
+ version = "0.15.6"
1653
+ source = { registry = "https://pypi.org/simple" }
1654
+ sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" }
1655
+ wheels = [
1656
+ { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" },
1657
+ { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" },
1658
+ { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" },
1659
+ { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" },
1660
+ { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" },
1661
+ { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" },
1662
+ { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" },
1663
+ { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" },
1664
+ { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" },
1665
+ { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" },
1666
+ { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" },
1667
+ { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" },
1668
+ { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" },
1669
+ { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" },
1670
+ { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" },
1671
+ { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" },
1672
+ { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" },
1563
1673
  ]
1564
1674
 
1565
1675
  [[package]]
@@ -1662,31 +1772,6 @@ wheels = [
1662
1772
  { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" },
1663
1773
  ]
1664
1774
 
1665
- [[package]]
1666
- name = "ty"
1667
- version = "0.0.12"
1668
- source = { registry = "https://pypi.org/simple" }
1669
- sdist = { url = "https://files.pythonhosted.org/packages/b5/78/ba1a4ad403c748fbba8be63b7e774a90e80b67192f6443d624c64fe4aaab/ty-0.0.12.tar.gz", hash = "sha256:cd01810e106c3b652a01b8f784dd21741de9fdc47bd595d02c122a7d5cefeee7", size = 4981303, upload-time = "2026-01-14T22:30:48.537Z" }
1670
- wheels = [
1671
- { url = "https://files.pythonhosted.org/packages/7d/8f/c21314d074dda5fb13d3300fa6733fd0d8ff23ea83a721818740665b6314/ty-0.0.12-py3-none-linux_armv6l.whl", hash = "sha256:eb9da1e2c68bd754e090eab39ed65edf95168d36cbeb43ff2bd9f86b4edd56d1", size = 9614164, upload-time = "2026-01-14T22:30:44.016Z" },
1672
- { url = "https://files.pythonhosted.org/packages/09/28/f8a4d944d13519d70c486e8f96d6fa95647ac2aa94432e97d5cfec1f42f6/ty-0.0.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c181f42aa19b0ed7f1b0c2d559980b1f1d77cc09419f51c8321c7ddf67758853", size = 9542337, upload-time = "2026-01-14T22:30:05.687Z" },
1673
- { url = "https://files.pythonhosted.org/packages/e1/9c/f576e360441de7a8201daa6dc4ebc362853bc5305e059cceeb02ebdd9a48/ty-0.0.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1f829e1eecd39c3e1b032149db7ae6a3284f72fc36b42436e65243a9ed1173db", size = 8909582, upload-time = "2026-01-14T22:30:46.089Z" },
1674
- { url = "https://files.pythonhosted.org/packages/d6/13/0898e494032a5d8af3060733d12929e3e7716db6c75eac63fa125730a3e7/ty-0.0.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45162e7826e1789cf3374627883cdeb0d56b82473a0771923e4572928e90be3", size = 9384932, upload-time = "2026-01-14T22:30:13.769Z" },
1675
- { url = "https://files.pythonhosted.org/packages/e4/1a/b35b6c697008a11d4cedfd34d9672db2f0a0621ec80ece109e13fca4dfef/ty-0.0.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d11fec40b269bec01e751b2337d1c7ffa959a2c2090a950d7e21c2792442cccd", size = 9453140, upload-time = "2026-01-14T22:30:11.131Z" },
1676
- { url = "https://files.pythonhosted.org/packages/dd/1e/71c9edbc79a3c88a0711324458f29c7dbf6c23452c6e760dc25725483064/ty-0.0.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09d99e37e761a4d2651ad9d5a610d11235fbcbf35dc6d4bc04abf54e7cf894f1", size = 9960680, upload-time = "2026-01-14T22:30:33.621Z" },
1677
- { url = "https://files.pythonhosted.org/packages/0e/75/39375129f62dd22f6ad5a99cd2a42fd27d8b91b235ce2db86875cdad397d/ty-0.0.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d9ca0cdb17bd37397da7b16a7cd23423fc65c3f9691e453ad46c723d121225a1", size = 10904518, upload-time = "2026-01-14T22:30:08.464Z" },
1678
- { url = "https://files.pythonhosted.org/packages/32/5e/26c6d88fafa11a9d31ca9f4d12989f57782ec61e7291d4802d685b5be118/ty-0.0.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcf2757b905e7eddb7e456140066335b18eb68b634a9f72d6f54a427ab042c64", size = 10525001, upload-time = "2026-01-14T22:30:16.454Z" },
1679
- { url = "https://files.pythonhosted.org/packages/c2/a5/2f0b91894af13187110f9ad7ee926d86e4e6efa755c9c88a820ed7f84c85/ty-0.0.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00cf34c1ebe1147efeda3021a1064baa222c18cdac114b7b050bbe42deb4ca80", size = 10307103, upload-time = "2026-01-14T22:30:41.221Z" },
1680
- { url = "https://files.pythonhosted.org/packages/4b/77/13d0410827e4bc713ebb7fdaf6b3590b37dcb1b82e0a81717b65548f2442/ty-0.0.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb3a655bd869352e9a22938d707631ac9fbca1016242b1f6d132d78f347c851", size = 10072737, upload-time = "2026-01-14T22:30:51.783Z" },
1681
- { url = "https://files.pythonhosted.org/packages/e1/dd/fc36d8bac806c74cf04b4ca735bca14d19967ca84d88f31e121767880df1/ty-0.0.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4658e282c7cb82be304052f8f64f9925f23c3c4f90eeeb32663c74c4b095d7ba", size = 9368726, upload-time = "2026-01-14T22:30:18.683Z" },
1682
- { url = "https://files.pythonhosted.org/packages/54/70/9e8e461647550f83e2fe54bc632ccbdc17a4909644783cdbdd17f7296059/ty-0.0.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c167d838eaaa06e03bb66a517f75296b643d950fbd93c1d1686a187e5a8dbd1f", size = 9454704, upload-time = "2026-01-14T22:30:22.759Z" },
1683
- { url = "https://files.pythonhosted.org/packages/04/9b/6292cf7c14a0efeca0539cf7d78f453beff0475cb039fbea0eb5d07d343d/ty-0.0.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2956e0c9ab7023533b461d8a0e6b2ea7b78e01a8dde0688e8234d0fce10c4c1c", size = 9649829, upload-time = "2026-01-14T22:30:31.234Z" },
1684
- { url = "https://files.pythonhosted.org/packages/49/bd/472a5d2013371e4870886cff791c94abdf0b92d43d305dd0f8e06b6ff719/ty-0.0.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c6a3fd7479580009f21002f3828320621d8a82d53b7ba36993234e3ccad58c8", size = 10162814, upload-time = "2026-01-14T22:30:36.174Z" },
1685
- { url = "https://files.pythonhosted.org/packages/31/e9/2ecbe56826759845a7c21d80aa28187865ea62bc9757b056f6cbc06f78ed/ty-0.0.12-py3-none-win32.whl", hash = "sha256:a91c24fd75c0f1796d8ede9083e2c0ec96f106dbda73a09fe3135e075d31f742", size = 9140115, upload-time = "2026-01-14T22:30:38.903Z" },
1686
- { url = "https://files.pythonhosted.org/packages/5d/6d/d9531eff35a5c0ec9dbc10231fac21f9dd6504814048e81d6ce1c84dc566/ty-0.0.12-py3-none-win_amd64.whl", hash = "sha256:df151894be55c22d47068b0f3b484aff9e638761e2267e115d515fcc9c5b4a4b", size = 9884532, upload-time = "2026-01-14T22:30:25.112Z" },
1687
- { url = "https://files.pythonhosted.org/packages/e9/f3/20b49e75967023b123a221134548ad7000f9429f13fdcdda115b4c26305f/ty-0.0.12-py3-none-win_arm64.whl", hash = "sha256:cea99d334b05629de937ce52f43278acf155d3a316ad6a35356635f886be20ea", size = 9313974, upload-time = "2026-01-14T22:30:27.44Z" },
1688
- ]
1689
-
1690
1775
  [[package]]
1691
1776
  name = "typer"
1692
1777
  version = "0.21.1"
@@ -1,74 +0,0 @@
1
- """Configuration service provider.
2
-
3
- This module provides the ConfigServiceProvider which is responsible for loading
4
- configuration files and registering the ConfigRepository into the container.
5
- """
6
-
7
- from pathlib import Path
8
- from typing import TYPE_CHECKING, Self, override
9
-
10
- from neva import Ok, Result
11
- from neva.arch import ServiceProvider
12
- from neva.config.loader import ConfigLoader
13
- from neva.config.repository import ConfigRepository
14
-
15
-
16
- if TYPE_CHECKING:
17
- from neva.arch import Application
18
-
19
-
20
- class ConfigServiceProvider(ServiceProvider):
21
- """Service provider for application configuration.
22
-
23
- Loads configuration files from the config directory and registers
24
- the ConfigRepository into the application container.
25
-
26
- Attributes:
27
- config_path: Path to the configuration directory.
28
- repository: The ConfigRepository instance after registration.
29
-
30
- """
31
-
32
- def __init__(
33
- self,
34
- app: "Application",
35
- config_path: str | Path | None = None,
36
- ) -> None:
37
- """Initialize the configuration service provider.
38
-
39
- Args:
40
- app: The application instance.
41
- config_path: Path to the configuration directory. Defaults to "./config".
42
-
43
- """
44
- self.config_path: Path = (
45
- Path(config_path) if config_path else Path.cwd() / "config"
46
- )
47
- self.repository: ConfigRepository | None = None
48
- super().__init__(app)
49
-
50
- @override
51
- def register(self) -> Result[Self, str]:
52
- repository = ConfigRepository()
53
-
54
- def load() -> ConfigRepository:
55
- loader = ConfigLoader(self.config_path)
56
- load_result = loader.load_all()
57
-
58
- if load_result.is_err:
59
- raise RuntimeError(
60
- f"Failed to load configuration: {load_result.unwrap_err()}"
61
- )
62
-
63
- configs = load_result.unwrap()
64
-
65
- for namespace, config_dict in configs.items():
66
- merge_result = repository.merge(namespace, config_dict)
67
- if merge_result.is_err:
68
- raise RuntimeError(merge_result.unwrap_err())
69
- return repository
70
-
71
- self.app.bind(load)
72
- self.repository = repository
73
-
74
- return Ok(self)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes