Coverage for pySDC / helpers / pysdc_helper.py: 95%

19 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-07 10:19 +0000

1import logging 

2 

3 

4class FrozenClass(object): 

5 """ 

6 Helper class to freeze a class, i.e. to avoid adding more attributes 

7 

8 Attributes: 

9 __isfrozen: Flag to freeze a class 

10 """ 

11 

12 __isfrozen = False 

13 

14 def __init_subclass__(cls, **kwargs): 

15 """ 

16 Called when a class inherits from FrozenClass. 

17 Creates a separate attrs list for each subclass. 

18 """ 

19 super().__init_subclass__(**kwargs) 

20 # Create a new attrs list for this specific subclass 

21 cls.attrs = [] 

22 

23 def __setattr__(self, key, value): 

24 """ 

25 Function called when setting attributes 

26 

27 Args: 

28 key: the attribute 

29 value: the value 

30 """ 

31 

32 # check if attribute exists and if class is frozen 

33 if self.__isfrozen and not (key in type(self).attrs or hasattr(self, key)): 

34 raise TypeError(f'{type(self).__name__!r} is a frozen class, cannot add attribute {key!r}') 

35 

36 object.__setattr__(self, key, value) 

37 

38 def __getattr__(self, key): 

39 """ 

40 This is needed in case the variables have not been initialized after adding. 

41 """ 

42 if key in type(self).attrs: 

43 return None 

44 else: 

45 return super().__getattribute__(key) 

46 

47 @classmethod 

48 def add_attr(cls, key, raise_error_if_exists=False): 

49 """ 

50 Add a key to the allowed attributes of this class. 

51 

52 Args: 

53 key (str): The key to add 

54 raise_error_if_exists (bool): Raise an error if the attribute already exists in the class 

55 """ 

56 logger = logging.getLogger(cls.__name__) 

57 if key in cls.attrs: 

58 if raise_error_if_exists: 

59 raise TypeError(f'Attribute {key!r} already exists in {cls.__name__}!') 

60 else: 

61 logger.debug(f'Skip adding attribute {key!r} because it already exists in {cls.__name__}!') 

62 else: 

63 cls.attrs += [key] 

64 logger.debug(f'Added attribute {key!r} to {cls.__name__}') 

65 

66 def _freeze(self): 

67 """ 

68 Function to freeze the class 

69 """ 

70 self.__isfrozen = True 

71 

72 def get(self, key, default=None): 

73 """ 

74 Wrapper for `__dict__.get` to use when reading variables that might not exist, depending on the configuration 

75 

76 Args: 

77 key (str): Name of the variable you wish to read 

78 default: Value to be returned if the variable does not exist 

79 

80 Returns: 

81 __dict__.get(key, default) 

82 """ 

83 return self.__dict__.get(key, default) 

84 

85 def __dir__(self): 

86 """ 

87 My hope is that some editors can use this for dynamic autocompletion. 

88 """ 

89 return super().__dir__() + type(self).attrs