python-neva 2.2.1__tar.gz → 2.4.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 (122) hide show
  1. {python_neva-2.2.1 → python_neva-2.4.0}/PKG-INFO +2 -1
  2. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/application.py +6 -2
  3. {python_neva-2.2.1 → python_neva-2.4.0}/neva/config/repository.py +8 -2
  4. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/manager.py +2 -0
  5. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/provider.py +1 -2
  6. python_neva-2.4.0/neva/obs/instrumentation/__init__.py +1 -0
  7. python_neva-2.4.0/neva/obs/instrumentation/sqlalchemy.py +15 -0
  8. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/encryption/encrypter.py +10 -13
  9. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/hash_manager.py +23 -31
  10. {python_neva-2.2.1 → python_neva-2.4.0}/pyproject.toml +2 -1
  11. {python_neva-2.2.1 → python_neva-2.4.0}/uv.lock +145 -1
  12. {python_neva-2.2.1 → python_neva-2.4.0}/.envrc +0 -0
  13. {python_neva-2.2.1 → python_neva-2.4.0}/.gitignore +0 -0
  14. {python_neva-2.2.1 → python_neva-2.4.0}/.pre-commit-config.yaml +0 -0
  15. {python_neva-2.2.1 → python_neva-2.4.0}/.python-version +0 -0
  16. {python_neva-2.2.1 → python_neva-2.4.0}/README.md +0 -0
  17. {python_neva-2.2.1 → python_neva-2.4.0}/neva/__init__.py +0 -0
  18. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/__init__.py +0 -0
  19. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/app.py +0 -0
  20. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/config.py +0 -0
  21. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/facade.py +0 -0
  22. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/faststream.py +0 -0
  23. {python_neva-2.2.1 → python_neva-2.4.0}/neva/arch/service_provider.py +0 -0
  24. {python_neva-2.2.1 → python_neva-2.4.0}/neva/config/__init__.py +0 -0
  25. {python_neva-2.2.1 → python_neva-2.4.0}/neva/config/base_providers.py +0 -0
  26. {python_neva-2.2.1 → python_neva-2.4.0}/neva/config/loader.py +0 -0
  27. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/__init__.py +0 -0
  28. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/config.py +0 -0
  29. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/connection.py +0 -0
  30. {python_neva-2.2.1 → python_neva-2.4.0}/neva/database/transaction.py +0 -0
  31. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/__init__.py +0 -0
  32. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/dispatcher.py +0 -0
  33. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/event.py +0 -0
  34. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/event_registry.py +0 -0
  35. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/listener.py +0 -0
  36. {python_neva-2.2.1 → python_neva-2.4.0}/neva/events/provider.py +0 -0
  37. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/__init__.py +0 -0
  38. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/logging/__init__.py +0 -0
  39. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/logging/manager.py +0 -0
  40. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/logging/provider.py +0 -0
  41. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/middleware/__init__.py +0 -0
  42. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/middleware/correlation.py +0 -0
  43. {python_neva-2.2.1 → python_neva-2.4.0}/neva/obs/middleware/profiler.py +0 -0
  44. {python_neva-2.2.1 → python_neva-2.4.0}/neva/py.typed +0 -0
  45. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/__init__.py +0 -0
  46. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/encryption/__init__.py +0 -0
  47. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/encryption/protocol.py +0 -0
  48. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/__init__.py +0 -0
  49. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/config.py +0 -0
  50. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/hashers/__init__.py +0 -0
  51. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/hashers/argon2.py +0 -0
  52. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/hashers/bcrypt.py +0 -0
  53. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/hashing/hashers/protocol.py +0 -0
  54. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/provider.py +0 -0
  55. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/tokens/__init__.py +0 -0
  56. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/tokens/generate_token.py +0 -0
  57. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/tokens/hash_token.py +0 -0
  58. {python_neva-2.2.1 → python_neva-2.4.0}/neva/security/tokens/verify_token.py +0 -0
  59. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/__init__.py +0 -0
  60. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/accessors.py +0 -0
  61. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/__init__.py +0 -0
  62. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/app.py +0 -0
  63. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/app.pyi +0 -0
  64. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/config.py +0 -0
  65. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/config.pyi +0 -0
  66. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/crypt.py +0 -0
  67. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/crypt.pyi +0 -0
  68. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/db.py +0 -0
  69. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/db.pyi +0 -0
  70. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/event.py +0 -0
  71. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/event.pyi +0 -0
  72. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/hash.py +0 -0
  73. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/hash.pyi +0 -0
  74. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/log.py +0 -0
  75. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/facade/log.pyi +0 -0
  76. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/results.py +0 -0
  77. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/strategy.py +0 -0
  78. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/strconv.py +0 -0
  79. {python_neva-2.2.1 → python_neva-2.4.0}/neva/support/time.py +0 -0
  80. {python_neva-2.2.1 → python_neva-2.4.0}/neva/testing/__init__.py +0 -0
  81. {python_neva-2.2.1 → python_neva-2.4.0}/neva/testing/fakes.py +0 -0
  82. {python_neva-2.2.1 → python_neva-2.4.0}/neva/testing/fixtures.py +0 -0
  83. {python_neva-2.2.1 → python_neva-2.4.0}/neva/testing/http.py +0 -0
  84. {python_neva-2.2.1 → python_neva-2.4.0}/neva/testing/test_case.py +0 -0
  85. {python_neva-2.2.1 → python_neva-2.4.0}/ruff.toml +0 -0
  86. {python_neva-2.2.1 → python_neva-2.4.0}/tests/__init__.py +0 -0
  87. {python_neva-2.2.1 → python_neva-2.4.0}/tests/arch/__init__.py +0 -0
  88. {python_neva-2.2.1 → python_neva-2.4.0}/tests/arch/test_scope.py +0 -0
  89. {python_neva-2.2.1 → python_neva-2.4.0}/tests/config/__init__.py +0 -0
  90. {python_neva-2.2.1 → python_neva-2.4.0}/tests/config/test_loader.py +0 -0
  91. {python_neva-2.2.1 → python_neva-2.4.0}/tests/config/test_repository.py +0 -0
  92. {python_neva-2.2.1 → python_neva-2.4.0}/tests/conftest.py +0 -0
  93. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/__init__.py +0 -0
  94. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/conftest.py +0 -0
  95. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_connection_manager.py +0 -0
  96. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_database_manager.py +0 -0
  97. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_edge_cases.py +0 -0
  98. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_multi_connection.py +0 -0
  99. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_sqlalchemy_integration.py +0 -0
  100. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_transaction.py +0 -0
  101. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_transaction_context.py +0 -0
  102. {python_neva-2.2.1 → python_neva-2.4.0}/tests/database/test_transaction_registry.py +0 -0
  103. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/__init__.py +0 -0
  104. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/conftest.py +0 -0
  105. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_deferred.py +0 -0
  106. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_dispatch.py +0 -0
  107. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_event.py +0 -0
  108. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_event_registry.py +0 -0
  109. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_function_listener.py +0 -0
  110. {python_neva-2.2.1 → python_neva-2.4.0}/tests/events/test_immediate.py +0 -0
  111. {python_neva-2.2.1 → python_neva-2.4.0}/tests/obs/__init__.py +0 -0
  112. {python_neva-2.2.1 → python_neva-2.4.0}/tests/obs/test_correlation.py +0 -0
  113. {python_neva-2.2.1 → python_neva-2.4.0}/tests/obs/test_profiler.py +0 -0
  114. {python_neva-2.2.1 → python_neva-2.4.0}/tests/security/__init__.py +0 -0
  115. {python_neva-2.2.1 → python_neva-2.4.0}/tests/security/test_encrypter.py +0 -0
  116. {python_neva-2.2.1 → python_neva-2.4.0}/tests/security/test_hash_manager.py +0 -0
  117. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/__init__.py +0 -0
  118. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/test_event_fake.py +0 -0
  119. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/test_facade_restore.py +0 -0
  120. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/test_fixtures.py +0 -0
  121. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/test_refresh_database.py +0 -0
  122. {python_neva-2.2.1 → python_neva-2.4.0}/tests/testing/test_test_case.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-neva
3
- Version: 2.2.1
3
+ Version: 2.4.0
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: aiosqlite>=0.20.0
@@ -9,6 +9,7 @@ Requires-Dist: cryptography>=46.0.3
9
9
  Requires-Dist: dishka>=1.7.2
10
10
  Requires-Dist: fastapi[all]>=0.129.0
11
11
  Requires-Dist: faststream>=0.6.6
12
+ Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.62b0
12
13
  Requires-Dist: pwdlib[argon2,bcrypt]>=0.3.0
13
14
  Requires-Dist: pyinstrument>=5.1.1
14
15
  Requires-Dist: sqlalchemy[asyncio]>=2.0.0
@@ -52,8 +52,12 @@ class Application:
52
52
 
53
53
  self.bind(lambda: self.config, interface=ConfigRepository)
54
54
 
55
- providers_from_file = self.config.get("providers.providers").unwrap_or([])
56
- providers_from_app = self.config.get("app.providers").unwrap_or([])
55
+ providers_from_file: list[type[ServiceProvider]] = self.config.get(
56
+ "providers.providers", type_=list[type[ServiceProvider]]
57
+ ).unwrap_or([])
58
+ providers_from_app = self.config.get(
59
+ "app.providers", type_=list[type[ServiceProvider]]
60
+ ).unwrap_or([])
57
61
  providers: set[type[ServiceProvider]] = set(providers_from_file).union(
58
62
  set(providers_from_app)
59
63
  )
@@ -60,12 +60,18 @@ class ConfigRepository:
60
60
  current[keys[-1]] = value
61
61
  return Ok(None)
62
62
 
63
- def get(self, key: str, default: object = None) -> Result[Any, str]:
63
+ def get[T](
64
+ self,
65
+ key: str,
66
+ default: T | None = None,
67
+ type_: type[T] | None = None,
68
+ ) -> Result[T, str]:
64
69
  """Get a configuration value using dot notation.
65
70
 
66
71
  Args:
67
72
  key: Dot-notated key path (e.g., "database.host").
68
73
  default: Default value to return if key is not found.
74
+ type_: Type to cast the value to.
69
75
 
70
76
  Returns:
71
77
  Result containing the configuration value or the default.
@@ -81,7 +87,7 @@ class ConfigRepository:
81
87
  return Ok(default)
82
88
  return Err(f"Config key '{key}' not found")
83
89
  current = current[k]
84
- return Ok(current)
90
+ return Ok(current) # type: ignore [arg-type]
85
91
  except KeyError:
86
92
  if default is not None:
87
93
  return Ok(default)
@@ -10,6 +10,7 @@ from neva import Nothing, Option, Some
10
10
  from neva.database.connection import ConnectionManager, TransactionContext
11
11
  from neva.database.transaction import BoundTransaction, Transaction
12
12
  from neva.obs import LogManager
13
+ from neva.obs.instrumentation.sqlalchemy import instrument
13
14
 
14
15
 
15
16
  @final
@@ -30,6 +31,7 @@ class DatabaseManager:
30
31
  name: The connection name.
31
32
  engine: The async engine.
32
33
  """
34
+ instrument(engine)
33
35
  self._engines[name] = engine
34
36
  self._session_factories[name] = async_sessionmaker(
35
37
  bind=engine,
@@ -8,7 +8,6 @@ from sqlalchemy.ext.asyncio import create_async_engine
8
8
 
9
9
  from neva import Err, Ok, Result
10
10
  from neva.arch import ServiceProvider
11
- from neva.config.repository import ConfigRepository
12
11
  from neva.database.connection import TransactionContext
13
12
  from neva.database.manager import DatabaseManager
14
13
  from neva.obs import LogManager
@@ -29,7 +28,7 @@ class DatabaseServiceProvider(ServiceProvider):
29
28
  logger: LogManager = self.app.make(LogManager).unwrap()
30
29
  db: DatabaseManager = self.app.make(DatabaseManager).unwrap()
31
30
  logger.info("Beginning SQLAlchemy initialization...")
32
- match self.app.make(ConfigRepository).unwrap().get("database"):
31
+ match self.app.config.get("database"):
33
32
  case Ok(config):
34
33
  connections: dict = config.get("connections", {})
35
34
  for name, conn_config in connections.items():
@@ -0,0 +1 @@
1
+ """Various OTel instrumentation."""
@@ -0,0 +1,15 @@
1
+ """SQLAlchemy instrumentation."""
2
+
3
+ from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
4
+ from sqlalchemy import Engine
5
+ from sqlalchemy.ext.asyncio import AsyncEngine
6
+
7
+
8
+ def instrument(engine: AsyncEngine | Engine) -> None:
9
+ """Instrument SQLAlchemy engine."""
10
+ match engine:
11
+ case AsyncEngine():
12
+ to_instrument = engine.sync_engine
13
+ case Engine():
14
+ to_instrument = engine
15
+ SQLAlchemyInstrumentor().instrument(engine=to_instrument)
@@ -10,7 +10,6 @@ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
10
10
 
11
11
  from neva import Err, Ok, Result
12
12
  from neva.arch import Application
13
- from neva.config import ConfigRepository
14
13
  from neva.security.encryption.protocol import JsonValue
15
14
 
16
15
 
@@ -127,18 +126,16 @@ class AesEncrypter:
127
126
  Raises:
128
127
  ValueError: If no encryption key is configured.
129
128
  """
130
- config = self._app.make(ConfigRepository).expect(
131
- "ConfigRepository not found in container"
132
- )
133
-
134
- key = config.get("app.key", default=None).unwrap_or(None)
135
- previous_keys = config.get("app.previous_keys", default=[]).unwrap_or([])
136
-
137
- if key is None:
138
- msg = "No encryption key configured. Set 'app.key' in configuration."
139
- raise ValueError(msg)
140
-
141
- ciphers = [AESGCM(self._parse_key(key))]
129
+ previous_keys = self._app.config.get(
130
+ "app.previous_keys", default=[], type_=list[str]
131
+ ).unwrap_or([])
132
+
133
+ match self._app.config.get("app.key", type_=str):
134
+ case Err():
135
+ msg = "No encryption key configured. Set 'app.key' in configuration."
136
+ raise ValueError(msg)
137
+ case Ok(key):
138
+ ciphers = [AESGCM(self._parse_key(key))]
142
139
 
143
140
  for prev_key in previous_keys:
144
141
  ciphers.append(AESGCM(self._parse_key(prev_key)))
@@ -1,10 +1,10 @@
1
1
  """Hash manager for managing password hashing strategies."""
2
2
 
3
- from typing import override
3
+ from typing import Literal, cast, override
4
4
 
5
- from neva import Option, from_optional
5
+ from neva import Option
6
6
  from neva.arch import Application
7
- from neva.config import ConfigRepository
7
+ from neva.security.hashing.config import Argon2Config, BcryptConfig
8
8
  from neva.security.hashing.hashers.argon2 import Argon2Hasher
9
9
  from neva.security.hashing.hashers.bcrypt import BcryptHasher
10
10
  from neva.security.hashing.hashers.protocol import Hasher
@@ -32,13 +32,7 @@ class HashManager(StrategyResolver[Hasher]):
32
32
  Returns:
33
33
  Option containing the default hasher name.
34
34
  """
35
- config_result = self.app.make(ConfigRepository)
36
- if config_result.is_err:
37
- return from_optional(None)
38
-
39
- config = config_result.unwrap()
40
- driver = config.get("hashing.driver", default=None).unwrap_or(None)
41
- return from_optional(driver)
35
+ return self.app.config.get("hashing.driver", type_=str).ok()
42
36
 
43
37
  def make(
44
38
  self,
@@ -113,18 +107,17 @@ class HashManager(StrategyResolver[Hasher]):
113
107
  Returns:
114
108
  Configured Argon2Hasher instance.
115
109
  """
116
- config = manager.app.make(ConfigRepository).unwrap()
117
- argon_config = config.get("hashing.argon", default={}).unwrap()
118
-
119
- if isinstance(argon_config, dict):
120
- return Argon2Hasher(
121
- time_cost=argon_config.get("time_cost", 2),
122
- memory_cost=argon_config.get("memory_cost", 102400),
123
- parallelism=argon_config.get("parallelism", 8),
124
- hash_len=argon_config.get("hash_len", 16),
125
- salt_len=argon_config.get("salt_len", 16),
126
- )
127
- return Argon2Hasher()
110
+ argon_config = manager.app.config.get(
111
+ "hashing.argon", default={}, type_=Argon2Config
112
+ ).unwrap()
113
+
114
+ return Argon2Hasher(
115
+ time_cost=cast(int, argon_config.get("time_cost", 2)),
116
+ memory_cost=cast(int, argon_config.get("memory_cost", 102400)),
117
+ parallelism=cast(int, argon_config.get("parallelism", 8)),
118
+ hash_len=cast(int, argon_config.get("hash_len", 16)),
119
+ salt_len=cast(int, argon_config.get("salt_len", 16)),
120
+ )
128
121
 
129
122
  def _create_bcrypt_hasher(self, manager: StrategyResolver[Hasher]) -> BcryptHasher:
130
123
  """Create an instance of the Bcrypt hash strategy.
@@ -135,12 +128,11 @@ class HashManager(StrategyResolver[Hasher]):
135
128
  Returns:
136
129
  Configured BcryptHasher instance.
137
130
  """
138
- config = manager.app.make(ConfigRepository).unwrap()
139
- bcrypt_config = config.get("hashing.bcrypt", default={}).unwrap()
140
-
141
- if isinstance(bcrypt_config, dict):
142
- return BcryptHasher(
143
- rounds=bcrypt_config.get("rounds", 12),
144
- prefix=bcrypt_config.get("prefix", "2b"),
145
- )
146
- return BcryptHasher()
131
+ bcrypt_config = manager.app.config.get(
132
+ "hashing.bcrypt", default={}, type_=BcryptConfig
133
+ ).unwrap()
134
+
135
+ return BcryptHasher(
136
+ rounds=cast(int, bcrypt_config.get("rounds", 12)),
137
+ prefix=cast(Literal["2a", "2b"], bcrypt_config.get("prefix", "2b")),
138
+ )
@@ -7,7 +7,7 @@ packages = ["neva"]
7
7
 
8
8
  [project]
9
9
  name = "python-neva"
10
- version = "2.2.1"
10
+ version = "2.4.0"
11
11
  description = "Add your description here"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.12"
@@ -23,6 +23,7 @@ dependencies = [
23
23
  "asyncpg>=0.30.0",
24
24
  "aiosqlite>=0.20.0",
25
25
  "typer>=0.21.1",
26
+ "opentelemetry-instrumentation-sqlalchemy>=0.62b0",
26
27
  ]
27
28
 
28
29
  [project.optional-dependencies]
@@ -795,6 +795,18 @@ wheels = [
795
795
  { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
796
796
  ]
797
797
 
798
+ [[package]]
799
+ name = "importlib-metadata"
800
+ version = "8.7.1"
801
+ source = { registry = "https://pypi.org/simple" }
802
+ dependencies = [
803
+ { name = "zipp" },
804
+ ]
805
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" }
806
+ wheels = [
807
+ { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" },
808
+ ]
809
+
798
810
  [[package]]
799
811
  name = "iniconfig"
800
812
  version = "2.3.0"
@@ -1020,6 +1032,63 @@ wheels = [
1020
1032
  { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
1021
1033
  ]
1022
1034
 
1035
+ [[package]]
1036
+ name = "opentelemetry-api"
1037
+ version = "1.41.0"
1038
+ source = { registry = "https://pypi.org/simple" }
1039
+ dependencies = [
1040
+ { name = "importlib-metadata" },
1041
+ { name = "typing-extensions" },
1042
+ ]
1043
+ sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" }
1044
+ wheels = [
1045
+ { url = "https://files.pythonhosted.org/packages/58/ee/99ab786653b3bda9c37ade7e24a7b607a1b1f696063172768417539d876d/opentelemetry_api-1.41.0-py3-none-any.whl", hash = "sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f", size = 69007, upload-time = "2026-04-09T14:38:11.833Z" },
1046
+ ]
1047
+
1048
+ [[package]]
1049
+ name = "opentelemetry-instrumentation"
1050
+ version = "0.62b0"
1051
+ source = { registry = "https://pypi.org/simple" }
1052
+ dependencies = [
1053
+ { name = "opentelemetry-api" },
1054
+ { name = "opentelemetry-semantic-conventions" },
1055
+ { name = "packaging" },
1056
+ { name = "wrapt" },
1057
+ ]
1058
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/fd/b8e90bb340957f059084376f94cff336b0e871a42feba7d3f7342365e987/opentelemetry_instrumentation-0.62b0.tar.gz", hash = "sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e", size = 34042, upload-time = "2026-04-09T14:40:22.843Z" }
1059
+ wheels = [
1060
+ { url = "https://files.pythonhosted.org/packages/00/b6/3356d2e335e3c449c5183e9b023f30f04f1b7073a6583c68745ea2e704b1/opentelemetry_instrumentation-0.62b0-py3-none-any.whl", hash = "sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c", size = 34158, upload-time = "2026-04-09T14:39:21.428Z" },
1061
+ ]
1062
+
1063
+ [[package]]
1064
+ name = "opentelemetry-instrumentation-sqlalchemy"
1065
+ version = "0.62b0"
1066
+ source = { registry = "https://pypi.org/simple" }
1067
+ dependencies = [
1068
+ { name = "opentelemetry-api" },
1069
+ { name = "opentelemetry-instrumentation" },
1070
+ { name = "opentelemetry-semantic-conventions" },
1071
+ { name = "packaging" },
1072
+ { name = "wrapt" },
1073
+ ]
1074
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/40adc8c38e5be017ceb230a28ca57ca81981d4dc0c4b902cc930c77fd14f/opentelemetry_instrumentation_sqlalchemy-0.62b0.tar.gz", hash = "sha256:d02f85b83f349e9ef70a34cb3f4c3a3481fa15b11747f09209818663e161cac4", size = 18539, upload-time = "2026-04-09T14:40:50.251Z" }
1075
+ wheels = [
1076
+ { url = "https://files.pythonhosted.org/packages/e7/e0/77954ac593f34740dc32e28a15fe7170e90f6ba6398eaaa5c88b34c05ed1/opentelemetry_instrumentation_sqlalchemy-0.62b0-py3-none-any.whl", hash = "sha256:ec576e0660080d9d15ce4fa44d2a07fff8cb4b796a84344cb0f2c9e5d6e26f79", size = 15534, upload-time = "2026-04-09T14:40:03.957Z" },
1077
+ ]
1078
+
1079
+ [[package]]
1080
+ name = "opentelemetry-semantic-conventions"
1081
+ version = "0.62b0"
1082
+ source = { registry = "https://pypi.org/simple" }
1083
+ dependencies = [
1084
+ { name = "opentelemetry-api" },
1085
+ { name = "typing-extensions" },
1086
+ ]
1087
+ sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" }
1088
+ wheels = [
1089
+ { url = "https://files.pythonhosted.org/packages/58/6c/5e86fa1759a525ef91c2d8b79d668574760ff3f900d114297765eb8786cb/opentelemetry_semantic_conventions-0.62b0-py3-none-any.whl", hash = "sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489", size = 231619, upload-time = "2026-04-09T14:38:32.394Z" },
1090
+ ]
1091
+
1023
1092
  [[package]]
1024
1093
  name = "orjson"
1025
1094
  version = "3.11.5"
@@ -1438,7 +1507,7 @@ wheels = [
1438
1507
 
1439
1508
  [[package]]
1440
1509
  name = "python-neva"
1441
- version = "2.2.1"
1510
+ version = "2.4.0"
1442
1511
  source = { editable = "." }
1443
1512
  dependencies = [
1444
1513
  { name = "aiosqlite" },
@@ -1447,6 +1516,7 @@ dependencies = [
1447
1516
  { name = "dishka" },
1448
1517
  { name = "fastapi", extra = ["all"] },
1449
1518
  { name = "faststream" },
1519
+ { name = "opentelemetry-instrumentation-sqlalchemy" },
1450
1520
  { name = "pwdlib", extra = ["argon2", "bcrypt"] },
1451
1521
  { name = "pyinstrument" },
1452
1522
  { name = "sqlalchemy", extra = ["asyncio"] },
@@ -1482,6 +1552,7 @@ requires-dist = [
1482
1552
  { name = "dishka", specifier = ">=1.7.2" },
1483
1553
  { name = "fastapi", extras = ["all"], specifier = ">=0.129.0" },
1484
1554
  { name = "faststream", specifier = ">=0.6.6" },
1555
+ { name = "opentelemetry-instrumentation-sqlalchemy", specifier = ">=0.62b0" },
1485
1556
  { name = "pwdlib", extras = ["argon2", "bcrypt"], specifier = ">=0.3.0" },
1486
1557
  { name = "pyinstrument", specifier = ">=5.1.1" },
1487
1558
  { name = "pytest", marker = "extra == 'testing'", specifier = ">=9.0.2" },
@@ -2062,3 +2133,76 @@ wheels = [
2062
2133
  { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" },
2063
2134
  { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
2064
2135
  ]
2136
+
2137
+ [[package]]
2138
+ name = "wrapt"
2139
+ version = "2.1.2"
2140
+ source = { registry = "https://pypi.org/simple" }
2141
+ sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" }
2142
+ wheels = [
2143
+ { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" },
2144
+ { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" },
2145
+ { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" },
2146
+ { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" },
2147
+ { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" },
2148
+ { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" },
2149
+ { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" },
2150
+ { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" },
2151
+ { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" },
2152
+ { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" },
2153
+ { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" },
2154
+ { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" },
2155
+ { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" },
2156
+ { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" },
2157
+ { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" },
2158
+ { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" },
2159
+ { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" },
2160
+ { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" },
2161
+ { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" },
2162
+ { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" },
2163
+ { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" },
2164
+ { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" },
2165
+ { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" },
2166
+ { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" },
2167
+ { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" },
2168
+ { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" },
2169
+ { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" },
2170
+ { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" },
2171
+ { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" },
2172
+ { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" },
2173
+ { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" },
2174
+ { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" },
2175
+ { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" },
2176
+ { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" },
2177
+ { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" },
2178
+ { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" },
2179
+ { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" },
2180
+ { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" },
2181
+ { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" },
2182
+ { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" },
2183
+ { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" },
2184
+ { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" },
2185
+ { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" },
2186
+ { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" },
2187
+ { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" },
2188
+ { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" },
2189
+ { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" },
2190
+ { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" },
2191
+ { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" },
2192
+ { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" },
2193
+ { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" },
2194
+ { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" },
2195
+ { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" },
2196
+ { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" },
2197
+ { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" },
2198
+ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" },
2199
+ ]
2200
+
2201
+ [[package]]
2202
+ name = "zipp"
2203
+ version = "3.23.0"
2204
+ source = { registry = "https://pypi.org/simple" }
2205
+ sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
2206
+ wheels = [
2207
+ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
2208
+ ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes