python-neva 2.3.0__py3-none-any.whl → 2.4.0__py3-none-any.whl

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.
neva/arch/application.py CHANGED
@@ -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
  )
neva/config/repository.py CHANGED
@@ -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)
neva/database/provider.py CHANGED
@@ -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
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-neva
3
- Version: 2.3.0
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
@@ -2,7 +2,7 @@ neva/__init__.py,sha256=Pj77_FvqTS-Xn0C1MZE34zTzl5QQcIyF1Zne64L37So,333
2
2
  neva/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  neva/arch/__init__.py,sha256=ZKMZ3u-pxVRRQcbqIF2L4F5IUclRcRVNe83hnN5YYeo,470
4
4
  neva/arch/app.py,sha256=devhJqAmssWCdhvT7d-mE6vyUuEXX5rIY06oIoR21QE,4451
5
- neva/arch/application.py,sha256=9bJCGKXX82xSN5LxmvGLYNSzbc8CgmWEk3DS9JqTMT8,5953
5
+ neva/arch/application.py,sha256=pBoN8ExT1_MXjeJhrRNdp_S3KdB7a9qf_Hc_sHNRei4,6096
6
6
  neva/arch/config.py,sha256=R8_wMKTwX8vTFqfQhgglDQ9PfB6Yo6mx6OnKg8bnIWw,1042
7
7
  neva/arch/facade.py,sha256=4aTOFuBwM58gn1FR7X1mOoKdi37J10OjPOpLVnCTCw8,7915
8
8
  neva/arch/faststream.py,sha256=1eUQRh3ahmQ6T991iHpxMeItDI53mEFq_30krIagNmM,2361
@@ -10,12 +10,12 @@ neva/arch/service_provider.py,sha256=g9tvlBNpGxdJ6yL9w6L-oPLzC6AV_Z6vLDR-TW3Kqz0
10
10
  neva/config/__init__.py,sha256=Ygfnh-MRRKYDdf54_LeMSHwmck5VpNHTdnnMtGEFLtU,121
11
11
  neva/config/base_providers.py,sha256=fK4feZWLzchpQ_xaeumtR9P2JK5nuk8Vq5O7JWkGyO8,908
12
12
  neva/config/loader.py,sha256=ZIcx8oYIi2r4wv1G_Q7_a6nG7wARyzjiZ2wF43t3B2I,3942
13
- neva/config/repository.py,sha256=B7iWgJLtOIwdmp7I0y9ISgl3g_Xc60tUe-ofDUKM6fg,4827
13
+ neva/config/repository.py,sha256=RlErYZfNBQQkwId3zXs3wjgcKR-uhUxNtNjh_3r9cD4,4972
14
14
  neva/database/__init__.py,sha256=3yYnEe8HQM86tURyAhTbMtE7AXubIEMN3GuwxkkT-bk,508
15
15
  neva/database/config.py,sha256=bVKUqlrYDFrURqg_FbntPXyVkbOUPSVXQJTKjyucjL0,451
16
16
  neva/database/connection.py,sha256=et1fvGxa2iHTJ8yzAv9VIYqfD4GtGaV0KQUW97SdLHQ,7107
17
17
  neva/database/manager.py,sha256=tQFHj2HZzo3oxdWyWNQlv5277I7vh7vDlEEiUyAKjDs,4511
18
- neva/database/provider.py,sha256=R-K4aFCgF8Ewu6I_-BXo874IYgQgI3b8qYPsGe7ArFA,1785
18
+ neva/database/provider.py,sha256=GvWZjT3f4BEzQFY9yB2QHciw2NXGoy6L-d1CbP6vPa4,1708
19
19
  neva/database/transaction.py,sha256=yQuFDhdXipUWac3zx4rVy6R6XUB_xPT_CLoQ-26223Y,5933
20
20
  neva/events/__init__.py,sha256=xwIAXNcOASpuCY4DJpshZc1gtxQHimzwy-s7c-4QuiQ,746
21
21
  neva/events/dispatcher.py,sha256=3xMhxE5uQBY7648jp75NkRwu09lqYo7ymmfSvnl6kT0,2850
@@ -35,11 +35,11 @@ neva/obs/middleware/profiler.py,sha256=ovx3s-39WpNtu2KhzVMWyIJa6OLrSVDuL56xyDA-v
35
35
  neva/security/__init__.py,sha256=Dhh3ZsseP6biWgRQY4hYKkoYFCR7ehhxz4VBqN8e02k,440
36
36
  neva/security/provider.py,sha256=20ZPKp4SxYV7pN1TN1yx-RIHken2TEHJBpaVjIFRxYI,499
37
37
  neva/security/encryption/__init__.py,sha256=eVWW-qk03vy7Bn-sNQ2WJHWZI6tPJOkknJz8llc1m0M,288
38
- neva/security/encryption/encrypter.py,sha256=_yo_jTU7yg3YloxvzjyUJBuXb-WAbnyYVTzuCF9F8JA,5238
38
+ neva/security/encryption/encrypter.py,sha256=E2DvTsG_756L75K1IUeYlEzazKn4Eqk1psryCk7HEHE,5156
39
39
  neva/security/encryption/protocol.py,sha256=H_JSt8IBH_AVkgccltY_4dGtcmESt4o76W0vxszBikM,839
40
40
  neva/security/hashing/__init__.py,sha256=k3SviEvtJLjBXTkR5I6w9YMPw6Q-LIoE1wGNz3GYvOE,374
41
41
  neva/security/hashing/config.py,sha256=ltj7_GW44gSg2N0i8CEYaw6Y6xUfzwsr4zXk5ic_sJI,624
42
- neva/security/hashing/hash_manager.py,sha256=ViFtbaKrbFLWJ6OpajFByqpmZ8YAnZfy_3Jatm0aWZ0,4866
42
+ neva/security/hashing/hash_manager.py,sha256=7Oy2gOUZvzRbMsD3unvE1HFDjcHvB-U3Gl84kd3Lbz4,4568
43
43
  neva/security/hashing/hashers/__init__.py,sha256=Zn_Q2tj_8lhhwJRZ7RXeJLYujd-3j7Rr8JQO2sSGgJs,44
44
44
  neva/security/hashing/hashers/argon2.py,sha256=pJqTl3_oCJCjJarnQwNSFRznlaijjbjdDVpC0DXDoSg,1402
45
45
  neva/security/hashing/hashers/bcrypt.py,sha256=H_15r-z9DYkd4HEbTz0QA4NHVBiFDXEqHkngP3HMSBY,1083
@@ -74,6 +74,6 @@ neva/testing/fakes.py,sha256=ffAAZC7WymuutVYk3vefuPDUvpNHav2QV2lj-Gw0Kic,2666
74
74
  neva/testing/fixtures.py,sha256=-l_Drw6nXTD_cKpfg1Z1pmsXnaD3NM2kihdRHxSkfzE,1657
75
75
  neva/testing/http.py,sha256=9oKNzaz38nCKkL-ZUF5CRWFPqpNmXkqbYcoukC509_Q,494
76
76
  neva/testing/test_case.py,sha256=FzXBHctkVkFNu46S5lnt7iRX91TLPLfL1Lzgn-iU-_E,3325
77
- python_neva-2.3.0.dist-info/METADATA,sha256=woz2CCGwVG7vPpl7C6tGjv2634D5iwO4WPeXzrPk8mw,691
78
- python_neva-2.3.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
79
- python_neva-2.3.0.dist-info/RECORD,,
77
+ python_neva-2.4.0.dist-info/METADATA,sha256=XyVNgtWT8cYqCGaFuRTgviKMZTOF7aKJaGTZz8nkUWc,691
78
+ python_neva-2.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
79
+ python_neva-2.4.0.dist-info/RECORD,,