Coverage for src / ocarina / opinionated / infra / env.py: 0.00%

13 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-06-04 15:56 +0200

1"""Typed environment variable accessor with key autocompletion support. 

2 

3Provides a generic, type-safe interface for accessing environment variables, 

4with IDE autocompletion and mypy validation on keys. 

5 

6Keys are defined as Literal types in the consuming project, giving full 

7static analysis support without any runtime overhead. 

8 

9Example: 

10 >>> import os 

11 >>> from typing import Literal 

12 >>> from types import MappingProxyType 

13 >>> 

14 >>> type MyCredKeys = Literal["intranet", "api_service"] 

15 >>> type MyValueKeys = Literal["expected_fullname"] 

16 >>> 

17 >>> def _creds(login_key: str, password_key: str) -> ImmutableCredentials: 

18 ... return MappingProxyType({ 

19 ... "login": os.environ[login_key], 

20 ... "password": os.environ[password_key], 

21 ... }) 

22 >>> 

23 >>> def create_env_getters() -> EnvGetters[MyCredKeys, MyValueKeys]: 

24 ... return EnvGetters( 

25 ... credentials={ 

26 ... "intranet": _creds("INTRANET_LOGIN", "INTRANET_PASSWORD"), 

27 ... "api_service": _creds("API_LOGIN", "API_PASSWORD"), 

28 ... }, 

29 ... values={ 

30 ... "expected_fullname": os.environ["EXPECTED_FULLNAME"], 

31 ... }, 

32 ... ) 

33 >>> 

34 >>> env = create_env_getters() 

35 >>> env.get_credentials("intranet") # ✅ autocomplete + mypy OK 

36 >>> env.get_credentials("unknown") # ❌ mypy error 

37 >>> env.get_value("expected_fullname") # ✅ autocomplete + mypy OK 

38 >>> env.get_value("typo") # ❌ mypy error 

39 

40""" 

41 

42from types import MappingProxyType 

43from typing import Literal 

44 

45ImmutableCredentialsKeys = Literal["login", "password"] 

46type ImmutableCredentials = MappingProxyType[ImmutableCredentialsKeys, str] 

47"""Read-only credential mapping. Prevents accidental mutation after retrieval.""" 

48 

49 

50class EnvGetters[TCredKeys: str, TValueKeys: str]: 

51 """Type-safe accessor for environment variables. 

52 

53 Generic over TCredKeys and TValueKeys — both are inferred from the 

54 dicts passed at construction, no manual annotation needed when using 

55 a factory function that declares the return type explicitly. 

56 

57 Args: 

58 credentials: Mapping of credential keys to immutable credential pairs. 

59 values: Mapping of value keys to string values. Optional. 

60 

61 """ 

62 

63 def __init__( 

64 self, 

65 credentials: dict[TCredKeys, ImmutableCredentials] | None = None, 

66 values: dict[TValueKeys, str] | None = None, 

67 ) -> None: 

68 """Initialize the env getter. 

69 

70 Args: 

71 credentials: Mapping of credential keys to immutable credential pairs. 

72 Keys become the valid arguments for get_credentials(). 

73 values: Mapping of value keys to string values. 

74 Keys become the valid arguments for get_value(). 

75 Defaults to empty dict if not provided. 

76 

77 """ 

78 self._credentials: dict[TCredKeys, ImmutableCredentials] = credentials or {} 

79 self._values: dict[TValueKeys, str] = values or {} 

80 

81 def get_credentials(self, k: TCredKeys) -> ImmutableCredentials: 

82 """Return the immutable credentials for the given key.""" 

83 return self._credentials[k] 

84 

85 def get_value(self, k: TValueKeys) -> str: 

86 """Return the string value for the given key.""" 

87 return self._values[k]