Coverage for src/configuraptor/alias.py: 100%

17 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-04 18:06 +0100

1""" 

2Alias functionality so config keys can have multiple names. 

3""" 

4import typing 

5from dataclasses import dataclass 

6from typing import Any 

7 

8from .abs import AnyType, T 

9 

10 

11@dataclass(frozen=True, slots=True) 

12class Alias: 

13 """ 

14 Internal class used to relate keys. 

15 """ 

16 

17 to: str 

18 

19 

20def alias(to: str) -> Any: 

21 """ 

22 Function to create an alias to a different key in the same class. 

23 """ 

24 return Alias(to) 

25 

26 

27def has_aliases(cls: AnyType, key: str) -> typing.Generator[str, None, None]: 

28 """ 

29 Generate all aliases that point to 'key' in 'cls'. 

30 """ 

31 for field, value in cls.__dict__.items(): 

32 if isinstance(value, Alias) and value.to == key: 

33 yield field 

34 

35 

36def has_alias(cls: AnyType, key: str, data: dict[str, T]) -> typing.Optional[T]: 

37 """ 

38 Get the value of any alias in the same config class that references `key`. 

39 

40 Example: 

41 class Config: 

42 key1: str 

43 key2: str = alias('key1') 

44 

45 load_into(Config, {'key2': 'something'}) 

46 # -> key1 will look up the value of key2 because it's configured as an alias for it. 

47 

48 If multiple aliases point to the same base, they are all iterated until a valid value was found. 

49 """ 

50 # for field, value in cls.__dict__.items(): 

51 # if isinstance(value, Alias) and value.to == key: 

52 # # yay! 

53 # return data.get(field) 

54 # 

55 # return None 

56 

57 return next( 

58 (value for field in has_aliases(cls, key) if (value := data.get(field))), 

59 None, 

60 ) 

61 

62 

63def is_alias(cls: AnyType, prop: str) -> bool: 

64 """ 

65 Returns whether 'prop' is an alias to something else on cls. 

66 """ 

67 return isinstance(cls.__dict__.get(prop), Alias)