Coverage for /home/ubuntu/Documents/Research/mut_p6/sacred/sacred/commands.py: 32%

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

114 statements  

1#!/usr/bin/env python 

2# coding=utf-8 

3"""Defines the stock-commands that every sacred experiment ships with.""" 

4import copy 

5 

6import pprint 

7import pydoc 

8import re 

9import sys 

10from collections import namedtuple, OrderedDict 

11 

12from colorama import Fore, Style 

13 

14from sacred.config import save_config_file 

15from sacred.serializer import flatten 

16from sacred.utils import PATHCHANGE, iterate_flattened_separately 

17 

18__all__ = ( 

19 "print_config", 

20 "print_dependencies", 

21 "save_config", 

22 "help_for_command", 

23 "print_named_configs", 

24) 

25 

26COLOR_DIRTY = Fore.RED 

27COLOR_TYPECHANGED = Fore.RED # prepend Style.BRIGHT for bold 

28COLOR_ADDED = Fore.GREEN 

29COLOR_MODIFIED = Fore.BLUE 

30COLOR_DOC = Style.DIM 

31ENDC = Style.RESET_ALL # '\033[0m' 

32 

33LEGEND = ( 

34 "(" 

35 + COLOR_MODIFIED 

36 + "modified" 

37 + ENDC 

38 + ", " 

39 + COLOR_ADDED 

40 + "added" 

41 + ENDC 

42 + ", " 

43 + COLOR_TYPECHANGED 

44 + "typechanged" 

45 + ENDC 

46 + ", " 

47 + COLOR_DOC 

48 + "doc" 

49 + ENDC 

50 + ")" 

51) 

52 

53ConfigEntry = namedtuple("ConfigEntry", "key value added modified typechanged doc") 

54PathEntry = namedtuple("PathEntry", "key added modified typechanged doc") 

55 

56 

57def _non_unicode_repr(objekt, context, maxlevels, level): 

58 """ 

59 Used to override the pprint format method to get rid of unicode prefixes. 

60 

61 E.g.: 'John' instead of u'John'. 

62 """ 

63 if sys.version_info[0] == 3 and sys.version_info[1] >= 8: 

64 repr_string, isreadable, isrecursive = pprint._safe_repr( 

65 objekt, context, maxlevels, level, sort_dicts=None 

66 ) 

67 else: 

68 repr_string, isreadable, isrecursive = pprint._safe_repr( 

69 objekt, context, maxlevels, level 

70 ) 

71 if repr_string.startswith('u"') or repr_string.startswith("u'"): 

72 repr_string = repr_string[1:] 

73 return repr_string, isreadable, isrecursive 

74 

75 

76PRINTER = pprint.PrettyPrinter() 

77PRINTER.format = _non_unicode_repr 

78 

79 

80def print_config(_run): 

81 """ 

82 Print the updated configuration and exit. 

83 

84 Text is highlighted: 

85 green: value modified 

86 blue: value added 

87 red: value modified but type changed 

88 """ 

89 final_config = _run.config 

90 config_mods = _run.config_modifications 

91 print(_format_config(final_config, config_mods)) 

92 

93 

94def _format_named_config(indent, path, named_config): 

95 indent = " " * indent 

96 assign = path 

97 if hasattr(named_config, "__doc__") and named_config.__doc__ is not None: 

98 doc_string = named_config.__doc__ 

99 if doc_string.strip().count("\n") == 0: 

100 assign += COLOR_DOC + " # {}".format(doc_string.strip()) + ENDC 

101 else: 

102 doc_string = doc_string.replace("\n", "\n" + indent) 

103 assign += ( 

104 COLOR_DOC + '\n{}"""{}"""'.format(indent + " ", doc_string) + ENDC 

105 ) 

106 return indent + assign 

107 

108 

109def _format_named_configs(named_configs, indent=2): 

110 lines = ["Named Configurations (" + COLOR_DOC + "doc" + ENDC + "):"] 

111 for path, named_config in named_configs.items(): 

112 lines.append(_format_named_config(indent, path, named_config)) 

113 if len(lines) < 2: 

114 lines.append(" " * indent + "No named configs") 

115 return "\n".join(lines) 

116 

117 

118def print_named_configs(ingredient): # noqa: D202 

119 """Returns a command that prints named configs recursively. 

120 

121 The command function prints the available named configs for the 

122 ingredient and all sub-ingredients and exits. 

123 

124 Example 

125 ------- 

126 The output is highlighted: 

127 white: config names 

128 grey: doc 

129 """ 

130 

131 def print_named_configs(): 

132 """Print the available named configs and exit.""" 

133 named_configs = OrderedDict(ingredient.gather_named_configs()) 

134 print(_format_named_configs(named_configs, 2)) 

135 

136 return print_named_configs 

137 

138 

139def help_for_command(command): 

140 """Get the help text (signature + docstring) for a command (function).""" 

141 help_text = pydoc.text.document(command) 

142 # remove backspaces 

143 return re.subn(".\\x08", "", help_text)[0] 

144 

145 

146def print_dependencies(_run): 

147 """Print the detected source-files and dependencies.""" 

148 print("Dependencies:") 

149 for dep in _run.experiment_info["dependencies"]: 

150 pack, _, version = dep.partition("==") 

151 print(" {:<20} == {}".format(pack, version)) 

152 

153 print("\nSources:") 

154 for source, digest in _run.experiment_info["sources"]: 

155 print(" {:<43} {}".format(source, digest)) 

156 

157 if _run.experiment_info["repositories"]: 

158 repos = _run.experiment_info["repositories"] 

159 print("\nVersion Control:") 

160 for repo in repos: 

161 mod = COLOR_DIRTY + "M" if repo["dirty"] else " " 

162 print("{} {:<43} {}".format(mod, repo["url"], repo["commit"]) + ENDC) 

163 print("") 

164 

165 

166def save_config(_config, _log, config_filename="config.json"): 

167 """ 

168 Store the updated configuration in a file. 

169 

170 By default uses the filename "config.json", but that can be changed by 

171 setting the config_filename config entry. 

172 """ 

173 # Copy the config to make it mutable 

174 _config = copy.deepcopy(_config) 

175 if "config_filename" in _config: 

176 del _config["config_filename"] 

177 _log.info('Saving config to "{}"'.format(config_filename)) 

178 save_config_file(flatten(_config), config_filename) 

179 

180 

181def _iterate_marked(cfg, config_mods): 

182 for path, value in iterate_flattened_separately(cfg, ["__doc__"]): 

183 if value is PATHCHANGE: 

184 yield path, PathEntry( 

185 key=path.rpartition(".")[2], 

186 added=path in config_mods.added, 

187 modified=path in config_mods.modified, 

188 typechanged=config_mods.typechanged.get(path), 

189 doc=config_mods.docs.get(path), 

190 ) 

191 else: 

192 yield path, ConfigEntry( 

193 key=path.rpartition(".")[2], 

194 value=value, 

195 added=path in config_mods.added, 

196 modified=path in config_mods.modified, 

197 typechanged=config_mods.typechanged.get(path), 

198 doc=config_mods.docs.get(path), 

199 ) 

200 

201 

202def _format_entry(indent, entry): 

203 color = "" 

204 indent = " " * indent 

205 if entry.typechanged: 

206 color = COLOR_TYPECHANGED # red 

207 elif entry.added: 

208 color = COLOR_ADDED # green 

209 elif entry.modified: 

210 color = COLOR_MODIFIED # blue 

211 if entry.key == "__doc__": 

212 color = COLOR_DOC # grey 

213 doc_string = entry.value.replace("\n", "\n" + indent) 

214 assign = '{}"""{}"""'.format(indent, doc_string) 

215 elif isinstance(entry, ConfigEntry): 

216 assign = indent + entry.key + " = " + PRINTER.pformat(entry.value) 

217 else: # isinstance(entry, PathEntry): 

218 assign = indent + entry.key + ":" 

219 if entry.doc: 

220 doc_string = COLOR_DOC + "# " + entry.doc + ENDC 

221 if len(assign) <= 35: 

222 assign = "{:<35} {}".format(assign, doc_string) 

223 else: 

224 assign += " " + doc_string 

225 end = ENDC if color else "" 

226 return color + assign + end 

227 

228 

229def _format_config(cfg, config_mods): 

230 lines = ["Configuration " + LEGEND + ":"] 

231 for path, entry in _iterate_marked(cfg, config_mods): 

232 indent = 2 + 2 * path.count(".") 

233 lines.append(_format_entry(indent, entry)) 

234 return "\n".join(lines)