python-neva 2.3.0__tar.gz → 2.4.1__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.3.0 → python_neva-2.4.1}/.pre-commit-config.yaml +1 -0
  2. {python_neva-2.3.0 → python_neva-2.4.1}/PKG-INFO +1 -1
  3. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/application.py +6 -2
  4. {python_neva-2.3.0 → python_neva-2.4.1}/neva/config/repository.py +10 -2
  5. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/provider.py +1 -2
  6. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/encryption/encrypter.py +10 -13
  7. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/hash_manager.py +23 -31
  8. {python_neva-2.3.0 → python_neva-2.4.1}/pyproject.toml +7 -1
  9. {python_neva-2.3.0 → python_neva-2.4.1}/uv.lock +1 -1
  10. {python_neva-2.3.0 → python_neva-2.4.1}/.envrc +0 -0
  11. {python_neva-2.3.0 → python_neva-2.4.1}/.gitignore +0 -0
  12. {python_neva-2.3.0 → python_neva-2.4.1}/.python-version +0 -0
  13. {python_neva-2.3.0 → python_neva-2.4.1}/README.md +0 -0
  14. {python_neva-2.3.0 → python_neva-2.4.1}/neva/__init__.py +0 -0
  15. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/__init__.py +0 -0
  16. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/app.py +0 -0
  17. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/config.py +0 -0
  18. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/facade.py +0 -0
  19. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/faststream.py +0 -0
  20. {python_neva-2.3.0 → python_neva-2.4.1}/neva/arch/service_provider.py +0 -0
  21. {python_neva-2.3.0 → python_neva-2.4.1}/neva/config/__init__.py +0 -0
  22. {python_neva-2.3.0 → python_neva-2.4.1}/neva/config/base_providers.py +0 -0
  23. {python_neva-2.3.0 → python_neva-2.4.1}/neva/config/loader.py +0 -0
  24. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/__init__.py +0 -0
  25. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/config.py +0 -0
  26. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/connection.py +0 -0
  27. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/manager.py +0 -0
  28. {python_neva-2.3.0 → python_neva-2.4.1}/neva/database/transaction.py +0 -0
  29. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/__init__.py +0 -0
  30. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/dispatcher.py +0 -0
  31. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/event.py +0 -0
  32. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/event_registry.py +0 -0
  33. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/listener.py +0 -0
  34. {python_neva-2.3.0 → python_neva-2.4.1}/neva/events/provider.py +0 -0
  35. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/__init__.py +0 -0
  36. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/instrumentation/__init__.py +0 -0
  37. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/instrumentation/sqlalchemy.py +0 -0
  38. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/logging/__init__.py +0 -0
  39. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/logging/manager.py +0 -0
  40. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/logging/provider.py +0 -0
  41. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/middleware/__init__.py +0 -0
  42. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/middleware/correlation.py +0 -0
  43. {python_neva-2.3.0 → python_neva-2.4.1}/neva/obs/middleware/profiler.py +0 -0
  44. {python_neva-2.3.0 → python_neva-2.4.1}/neva/py.typed +0 -0
  45. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/__init__.py +0 -0
  46. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/encryption/__init__.py +0 -0
  47. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/encryption/protocol.py +0 -0
  48. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/__init__.py +0 -0
  49. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/config.py +0 -0
  50. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/hashers/__init__.py +0 -0
  51. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/hashers/argon2.py +0 -0
  52. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/hashers/bcrypt.py +0 -0
  53. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/hashing/hashers/protocol.py +0 -0
  54. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/provider.py +0 -0
  55. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/tokens/__init__.py +0 -0
  56. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/tokens/generate_token.py +0 -0
  57. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/tokens/hash_token.py +0 -0
  58. {python_neva-2.3.0 → python_neva-2.4.1}/neva/security/tokens/verify_token.py +0 -0
  59. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/__init__.py +0 -0
  60. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/accessors.py +0 -0
  61. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/__init__.py +0 -0
  62. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/app.py +0 -0
  63. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/app.pyi +0 -0
  64. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/config.py +0 -0
  65. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/config.pyi +0 -0
  66. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/crypt.py +0 -0
  67. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/crypt.pyi +0 -0
  68. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/db.py +0 -0
  69. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/db.pyi +0 -0
  70. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/event.py +0 -0
  71. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/event.pyi +0 -0
  72. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/hash.py +0 -0
  73. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/hash.pyi +0 -0
  74. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/log.py +0 -0
  75. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/facade/log.pyi +0 -0
  76. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/results.py +0 -0
  77. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/strategy.py +0 -0
  78. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/strconv.py +0 -0
  79. {python_neva-2.3.0 → python_neva-2.4.1}/neva/support/time.py +0 -0
  80. {python_neva-2.3.0 → python_neva-2.4.1}/neva/testing/__init__.py +0 -0
  81. {python_neva-2.3.0 → python_neva-2.4.1}/neva/testing/fakes.py +0 -0
  82. {python_neva-2.3.0 → python_neva-2.4.1}/neva/testing/fixtures.py +0 -0
  83. {python_neva-2.3.0 → python_neva-2.4.1}/neva/testing/http.py +0 -0
  84. {python_neva-2.3.0 → python_neva-2.4.1}/neva/testing/test_case.py +0 -0
  85. {python_neva-2.3.0 → python_neva-2.4.1}/ruff.toml +0 -0
  86. {python_neva-2.3.0 → python_neva-2.4.1}/tests/__init__.py +0 -0
  87. {python_neva-2.3.0 → python_neva-2.4.1}/tests/arch/__init__.py +0 -0
  88. {python_neva-2.3.0 → python_neva-2.4.1}/tests/arch/test_scope.py +0 -0
  89. {python_neva-2.3.0 → python_neva-2.4.1}/tests/config/__init__.py +0 -0
  90. {python_neva-2.3.0 → python_neva-2.4.1}/tests/config/test_loader.py +0 -0
  91. {python_neva-2.3.0 → python_neva-2.4.1}/tests/config/test_repository.py +0 -0
  92. {python_neva-2.3.0 → python_neva-2.4.1}/tests/conftest.py +0 -0
  93. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/__init__.py +0 -0
  94. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/conftest.py +0 -0
  95. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_connection_manager.py +0 -0
  96. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_database_manager.py +0 -0
  97. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_edge_cases.py +0 -0
  98. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_multi_connection.py +0 -0
  99. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_sqlalchemy_integration.py +0 -0
  100. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_transaction.py +0 -0
  101. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_transaction_context.py +0 -0
  102. {python_neva-2.3.0 → python_neva-2.4.1}/tests/database/test_transaction_registry.py +0 -0
  103. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/__init__.py +0 -0
  104. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/conftest.py +0 -0
  105. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_deferred.py +0 -0
  106. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_dispatch.py +0 -0
  107. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_event.py +0 -0
  108. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_event_registry.py +0 -0
  109. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_function_listener.py +0 -0
  110. {python_neva-2.3.0 → python_neva-2.4.1}/tests/events/test_immediate.py +0 -0
  111. {python_neva-2.3.0 → python_neva-2.4.1}/tests/obs/__init__.py +0 -0
  112. {python_neva-2.3.0 → python_neva-2.4.1}/tests/obs/test_correlation.py +0 -0
  113. {python_neva-2.3.0 → python_neva-2.4.1}/tests/obs/test_profiler.py +0 -0
  114. {python_neva-2.3.0 → python_neva-2.4.1}/tests/security/__init__.py +0 -0
  115. {python_neva-2.3.0 → python_neva-2.4.1}/tests/security/test_encrypter.py +0 -0
  116. {python_neva-2.3.0 → python_neva-2.4.1}/tests/security/test_hash_manager.py +0 -0
  117. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/__init__.py +0 -0
  118. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/test_event_fake.py +0 -0
  119. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/test_facade_restore.py +0 -0
  120. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/test_fixtures.py +0 -0
  121. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/test_refresh_database.py +0 -0
  122. {python_neva-2.3.0 → python_neva-2.4.1}/tests/testing/test_test_case.py +0 -0
@@ -26,3 +26,4 @@ repos:
26
26
  rev: v1.19.1
27
27
  hooks:
28
28
  - id: mypy
29
+ args: [--enable-incomplete-feature=TypeForm]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-neva
3
- Version: 2.3.0
3
+ Version: 2.4.1
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: aiosqlite>=0.20.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
  )
@@ -7,6 +7,8 @@ nested access and can be frozen to prevent modifications after initialization.
7
7
 
8
8
  from typing import Any
9
9
 
10
+ from typing_extensions import TypeForm
11
+
10
12
  from neva import Err, Ok, Result
11
13
 
12
14
 
@@ -60,12 +62,18 @@ class ConfigRepository:
60
62
  current[keys[-1]] = value
61
63
  return Ok(None)
62
64
 
63
- def get(self, key: str, default: object = None) -> Result[Any, str]:
65
+ def get[T](
66
+ self,
67
+ key: str,
68
+ default: T | None = None,
69
+ type_: TypeForm[T] | None = None,
70
+ ) -> Result[T, str]:
64
71
  """Get a configuration value using dot notation.
65
72
 
66
73
  Args:
67
74
  key: Dot-notated key path (e.g., "database.host").
68
75
  default: Default value to return if key is not found.
76
+ type_: Type to cast the value to.
69
77
 
70
78
  Returns:
71
79
  Result containing the configuration value or the default.
@@ -81,7 +89,7 @@ class ConfigRepository:
81
89
  return Ok(default)
82
90
  return Err(f"Config key '{key}' not found")
83
91
  current = current[k]
84
- return Ok(current)
92
+ return Ok(current) # type: ignore [arg-type]
85
93
  except KeyError:
86
94
  if default is not None:
87
95
  return Ok(default)
@@ -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():
@@ -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.3.0"
10
+ version = "2.4.1"
11
11
  description = "Add your description here"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.12"
@@ -43,6 +43,12 @@ dev = [
43
43
  "ruff>=0.15.6",
44
44
  ]
45
45
 
46
+ [tool.basedpyright]
47
+ enableExperimentalFeatures = true
48
+
49
+ [tool.mypy]
50
+ enable_incomplete_feature = [ "TypeForm" ]
51
+
46
52
  [tool.pytest.ini_options]
47
53
  asyncio_mode = "auto"
48
54
  asyncio_default_fixture_loop_scope = "function"
@@ -1507,7 +1507,7 @@ wheels = [
1507
1507
 
1508
1508
  [[package]]
1509
1509
  name = "python-neva"
1510
- version = "2.3.0"
1510
+ version = "2.4.1"
1511
1511
  source = { editable = "." }
1512
1512
  dependencies = [
1513
1513
  { name = "aiosqlite" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes