Coverage for sacred/sacred/config/utils.py: 90%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

67 statements  

1#!/usr/bin/env python 

2# coding=utf-8 

3 

4import jsonpickle.tags 

5 

6from sacred import SETTINGS 

7import sacred.optional as opt 

8from sacred.config.custom_containers import DogmaticDict, DogmaticList 

9from sacred.utils import PYTHON_IDENTIFIER 

10 

11 

12def assert_is_valid_key(key): 

13 """ 

14 Raise KeyError if a given config key violates any requirements. 

15 

16 The requirements are the following and can be individually deactivated 

17 in ``sacred.SETTINGS.CONFIG_KEYS``: 

18 * ENFORCE_MONGO_COMPATIBLE (default: True): 

19 make sure the keys don't contain a '.' or start with a '$' 

20 * ENFORCE_JSONPICKLE_COMPATIBLE (default: True): 

21 make sure the keys do not contain any reserved jsonpickle tags 

22 This is very important. Only deactivate if you know what you are doing. 

23 * ENFORCE_STRING (default: False): 

24 make sure all keys are string. 

25 * ENFORCE_VALID_PYTHON_IDENTIFIER (default: False): 

26 make sure all keys are valid python identifiers. 

27 

28 Parameters 

29 ---------- 

30 key: 

31 The key that should be checked 

32 

33 Raises 

34 ------ 

35 KeyError: 

36 if the key violates any requirements 

37 

38 """ 

39 if SETTINGS.CONFIG.ENFORCE_KEYS_MONGO_COMPATIBLE and ( 

40 isinstance(key, str) and ("." in key or key[0] == "$") 

41 ): 

42 raise KeyError( 

43 'Invalid key "{}". Config-keys cannot ' 

44 'contain "." or start with "$"'.format(key) 

45 ) 

46 

47 if ( 

48 SETTINGS.CONFIG.ENFORCE_KEYS_JSONPICKLE_COMPATIBLE 

49 and isinstance(key, str) 

50 and (key in jsonpickle.tags.RESERVED or key.startswith("json://")) 

51 ): 

52 raise KeyError( 

53 'Invalid key "{}". Config-keys cannot be one of the' 

54 "reserved jsonpickle tags: {}".format(key, jsonpickle.tags.RESERVED) 

55 ) 

56 

57 if SETTINGS.CONFIG.ENFORCE_STRING_KEYS and (not isinstance(key, str)): 

58 raise KeyError( 

59 'Invalid key "{}". Config-keys have to be strings, ' 

60 "but was {}".format(key, type(key)) 

61 ) 

62 

63 if SETTINGS.CONFIG.ENFORCE_VALID_PYTHON_IDENTIFIER_KEYS and ( 

64 isinstance(key, str) and not PYTHON_IDENTIFIER.match(key) 

65 ): 

66 raise KeyError('Key "{}" is not a valid python identifier'.format(key)) 

67 

68 if SETTINGS.CONFIG.ENFORCE_KEYS_NO_EQUALS and (isinstance(key, str) and "=" in key): 

69 raise KeyError( 

70 'Invalid key "{}". Config keys may not contain an' 

71 'equals sign ("=").'.format("=") 

72 ) 

73 

74 

75def normalize_numpy(obj): 

76 if opt.has_numpy and isinstance(obj, opt.np.generic): 

77 try: 

78 return obj.item() 

79 except ValueError: 

80 pass 

81 return obj 

82 

83 

84def normalize_or_die(obj): 

85 if isinstance(obj, dict): 

86 res = dict() 

87 for key, value in obj.items(): 

88 assert_is_valid_key(key) 

89 res[key] = normalize_or_die(value) 

90 return res 

91 elif isinstance(obj, (list, tuple)): 

92 return list([normalize_or_die(value) for value in obj]) 

93 return normalize_numpy(obj) 

94 

95 

96def recursive_fill_in(config, preset): 

97 for key in preset: 

98 if key not in config: 

99 config[key] = preset[key] 

100 elif isinstance(config[key], dict) and isinstance(preset[key], dict): 

101 recursive_fill_in(config[key], preset[key]) 

102 

103 

104def chain_evaluate_config_scopes(config_scopes, fixed=None, preset=None, fallback=None): 

105 fixed = fixed or {} 

106 fallback = fallback or {} 

107 final_config = dict(preset or {}) 

108 config_summaries = [] 

109 for config in config_scopes: 

110 cfg = config(fixed=fixed, preset=final_config, fallback=fallback) 

111 config_summaries.append(cfg) 

112 final_config.update(cfg) 

113 

114 if not config_scopes: 

115 final_config.update(fixed) 

116 

117 return undogmatize(final_config), config_summaries 

118 

119 

120def dogmatize(obj): 

121 if isinstance(obj, dict): 

122 return DogmaticDict({key: dogmatize(val) for key, val in obj.items()}) 

123 elif isinstance(obj, list): 

124 return DogmaticList([dogmatize(value) for value in obj]) 

125 elif isinstance(obj, tuple): 

126 return tuple(dogmatize(value) for value in obj) 

127 else: 

128 return obj 

129 

130 

131def undogmatize(obj): 

132 if isinstance(obj, DogmaticDict): 

133 return dict({key: undogmatize(value) for key, value in obj.items()}) 

134 elif isinstance(obj, DogmaticList): 

135 return list([undogmatize(value) for value in obj]) 

136 elif isinstance(obj, tuple): 

137 return tuple(undogmatize(value) for value in obj) 

138 else: 

139 return obj