Coverage for src/configuraptor/postpone.py: 47%

1"""

2 File contains logic to do with the 'postpone' feature.

3"""

4

5 from typing import Any, Optional

6

7 from typing_extensions import Never

8

9 from .errors import IsPostponedError

10 from .singleton import Singleton

11

12

13 class Postponed(Singleton):

14 """

15 Class returned by `postpone` below.

16 """

17

18 def __get_property_name__(self, instance: type[Any], owner: type[Any]) -> Optional[str]:

19 """

20 Internal method to get the property name of the class this descriptor is being used on.

21 """

22 if not instance: # pragma: no cover

23 return None

24

25 # instance: the instance the descriptor is accessed from

26 # owner: the class that owns the descriptor

27 property_name = None

28 for attr_name, attr_value in owner.__dict__.items():

29 if attr_value is self:

30 property_name = attr_name

31 break

32 # return instance.__dict__.get(property_name, None)

33 return property_name

34

35 def __get__(self, instance: type[Any], owner: type[Any]) -> Never:

36 """

37 This magic method is called when a property is accessed.

38

39 Example:

40 someclass.someprop will trigger the __get__ of `someprop`

41

42 Args:

43 instance: the class on which the postponed property is defined,

44 owner: `SingletonMeta`

45 """

46 msg = f"Err: Using postponed property on {owner.__name__}"

47

48 if name := self.__get_property_name__(instance, owner):

49 msg += f".{name}"

50

51 raise IsPostponedError(msg)

52

53

54 def postpone() -> Any:

55 """

56 Can be used to mark a property as postponed, meaning the user will fill it in later (they promose).

57

58 If they don't fill it in and still try to use it, they will be met with a IsPostponedError.

59 """

60 return Postponed()