Completed
Push — master ( cdecc6...9c9404 )
by Klaus
02:35
created

_get_truncated_python_version()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
c 1
b 1
f 0
dl 0
loc 3
rs 10
1
#!/usr/bin/env python
2
# coding=utf-8
3
"""Defines the stock-commands that every sacred experiment ships with."""
4
from __future__ import division, print_function, unicode_literals
5
6
import pprint
7
import pydoc
8
import re
9
from collections import namedtuple
10
11
from sacred.config import save_config_file
12
from sacred.serializer import flatten
13
from sacred.utils import PATHCHANGE, iterate_flattened_separately
14
15
__sacred__ = True  # marks files that should be filtered from stack traces
16
17
__all__ = ('print_config', 'print_dependencies', 'save_config',
18
           'help_for_command')
19
20
BLUE = '\033[94m'
21
GREEN = '\033[92m'
22
RED = '\033[91m'
23
GREY = '\033[90m'
24
ENDC = '\033[0m'
25
26
LEGEND = '(' + BLUE + 'modified' + ENDC +\
27
    ', ' + GREEN + 'added' + ENDC +\
28
    ', ' + RED + 'typechanged' + ENDC +\
29
    ', ' + GREY + 'doc' + ENDC + ')'
30
31
ConfigEntry = namedtuple('ConfigEntry',
32
                         'key value added modified typechanged doc')
33
PathEntry = namedtuple('PathEntry', 'key added modified typechanged doc')
34
35
36
def _non_unicode_repr(objekt, context, maxlevels, level):
37
    """
38
    Used to override the pprint format method to get rid of unicode prefixes.
39
40
    E.g.: 'John' instead of u'John'.
41
    """
42
    repr_string, isreadable, isrecursive = pprint._safe_repr(objekt, context,
43
                                                             maxlevels, level)
44
    if repr_string.startswith('u"') or repr_string.startswith("u'"):
45
        repr_string = repr_string[1:]
46
    return repr_string, isreadable, isrecursive
47
48
49
PRINTER = pprint.PrettyPrinter()
50
PRINTER.format = _non_unicode_repr
51
52
53
def print_config(_run):
54
    """
55
    Print the updated configuration and exit.
56
57
    Text is highlighted:
58
      green:  value modified
59
      blue:   value added
60
      red:    value modified but type changed
61
    """
62
    final_config = _run.config
63
    config_mods = _run.config_modifications
64
    print(_format_config(final_config, config_mods))
65
66
67
def help_for_command(command):
68
    """Get the help text (signature + docstring) for a command (function)."""
69
    help_text = pydoc.text.document(command)
70
    # remove backspaces
71
    return re.subn('.\\x08', '', help_text)[0]
72
73
74
def print_dependencies(_run):
75
    """Print the detected source-files and dependencies."""
76
    print('Dependencies:')
77
    for dep in _run.experiment_info['dependencies']:
78
        pack, _, version = dep.partition('==')
79
        print('  {:<20} == {}'.format(pack, version))
80
81
    print('\nSources:')
82
    for source, digest in _run.experiment_info['sources']:
83
        print('  {:<43}  {}'.format(source, digest))
84
85
    if _run.experiment_info['repositories']:
86
        repos = _run.experiment_info['repositories']
87
        print('\nVersion Control:')
88
        for repo in repos:
89
            mod = RED + 'M' if repo['dirty'] else ' '
90
            print('{} {:<43}  {}'.format(mod, repo['url'], repo['commit']) +
91
                  ENDC)
92
    print('')
93
94
95
def save_config(_config, _log, config_filename='config.json'):
96
    """
97
    Store the updated configuration in a file.
98
99
    By default uses the filename "config.json", but that can be changed by
100
    setting the config_filename config entry.
101
    """
102
    if 'config_filename' in _config:
103
        del _config['config_filename']
104
    _log.info('Saving config to "{}"'.format(config_filename))
105
    save_config_file(flatten(_config), config_filename)
106
107
108
def _iterate_marked(cfg, config_mods):
109
    for path, value in iterate_flattened_separately(cfg, ['__doc__']):
110
        if value is PATHCHANGE:
111
            yield path, PathEntry(
112
                key=path.rpartition('.')[2],
113
                added=path in config_mods.added,
114
                modified=path in config_mods.modified,
115
                typechanged=config_mods.typechanged.get(path),
116
                doc=config_mods.docs.get(path))
117
        else:
118
            yield path, ConfigEntry(
119
                key=path.rpartition('.')[2],
120
                value=value,
121
                added=path in config_mods.added,
122
                modified=path in config_mods.modified,
123
                typechanged=config_mods.typechanged.get(path),
124
                doc=config_mods.docs.get(path))
125
126
127
def _format_entry(indent, entry):
128
    color = ""
129
    indent = ' ' * indent
130
    if entry.typechanged:
131
        color = RED
132
    elif entry.added:
133
        color = GREEN
134
    elif entry.modified:
135
        color = BLUE
136
    if entry.key == '__doc__':
137
        color = GREY
138
        doc_string = entry.value.replace('\n', '\n' + indent)
139
        assign = '{}"""{}"""'.format(indent, doc_string)
140
    elif isinstance(entry, ConfigEntry):
141
        assign = indent + entry.key + " = " + PRINTER.pformat(entry.value)
142
    else:  # isinstance(entry, PathEntry):
143
        assign = indent + entry.key + ":"
144
    if entry.doc:
145
        doc_string = GREY + '# ' + entry.doc + ENDC
146
        if len(assign) <= 35:
147
            assign = "{:<35}  {}".format(assign, doc_string)
148
        else:
149
            assign += '    ' + doc_string
150
    end = ENDC if color else ""
151
    return color + assign + end
152
153
154
def _format_config(cfg, config_mods):
155
    lines = ['Configuration ' + LEGEND + ':']
156
    for path, entry in _iterate_marked(cfg, config_mods):
157
        indent = 2 + 2 * path.count('.')
158
        lines.append(_format_entry(indent, entry))
159
    return "\n".join(lines)
160
161
162
def _write_file(base_dir, filename, content, mode='t'):
163
    full_name = os.path.join(base_dir, filename)
164
    os.makedirs(os.path.dirname(full_name), exist_ok=True)
165
    with open(full_name, 'w' + mode) as f:
166
        f.write(content)
167
168
169
def _get_truncated_python_version(host_info):
170
    version = parse_version(host_info['python_version'])
171
    return '{}.{}'.format(*version._version.release[:2])
172
173