IgnoreHostInfo   A
last analyzed

Complexity

Total Complexity 0

Size/Duplication

Total Lines 2
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env python
2
# coding=utf-8
3
"""This module helps to collect information about the host of an experiment."""
4
from __future__ import division, print_function, unicode_literals
5
6
import os
7
import platform
8
import re
9
import subprocess
10
import xml.etree.ElementTree as ET
11
12
import cpuinfo
13
14
from sacred.utils import optional_kwargs_decorator, FileNotFoundError
15
from sacred.settings import SETTINGS
16
17
__all__ = ('host_info_gatherers', 'get_host_info', 'host_info_getter')
18
19
host_info_gatherers = {}
20
"""Global dict of functions that are used to collect the host information."""
21
22
23
class IgnoreHostInfo(Exception):
24
    """Used by host_info_getters to signal that this cannot be gathered."""
25
26
27
def get_host_info():
28
    """Collect some information about the machine this experiment runs on.
29
30
    Returns
31
    -------
32
    dict
33
        A dictionary with information about the CPU, the OS and the
34
        Python version of this machine.
35
36
    """
37
    host_info = {}
38
    for k, v in host_info_gatherers.items():
39
        try:
40
            host_info[k] = v()
41
        except IgnoreHostInfo:
42
            pass
43
    return host_info
44
45
46
@optional_kwargs_decorator
47
def host_info_getter(func, name=None):
48
    """
49
    The decorated function is added to the process of collecting the host_info.
50
51
    This just adds the decorated function to the global
52
    ``sacred.host_info.host_info_gatherers`` dictionary.
53
    The functions from that dictionary are used when collecting the host info
54
    using :py:func:`~sacred.host_info.get_host_info`.
55
56
    Parameters
57
    ----------
58
    func : callable
59
        A function that can be called without arguments and returns some
60
        json-serializable information.
61
    name : str, optional
62
        The name of the corresponding entry in host_info.
63
        Defaults to the name of the function.
64
65
    Returns
66
    -------
67
    The function itself.
68
69
    """
70
    name = name or func.__name__
71
    host_info_gatherers[name] = func
72
    return func
73
74
75
# #################### Default Host Information ###############################
76
77
@host_info_getter(name='hostname')
78
def _hostname():
79
    return platform.node()
80
81
82
@host_info_getter(name='os')
83
def _os():
84
    return [platform.system(), platform.platform()]
85
86
87
@host_info_getter(name='python_version')
88
def _python_version():
89
    return platform.python_version()
90
91
92
@host_info_getter(name='cpu')
93
def _cpu():
94
    if platform.system() == "Windows":
95
        return _get_cpu_by_pycpuinfo()
96
    try:
97
        if platform.system() == "Darwin":
98
            return _get_cpu_by_sysctl()
99
        elif platform.system() == "Linux":
100
            return _get_cpu_by_proc_cpuinfo()
101
    except Exception:
102
        # Use pycpuinfo only if other ways fail, since it takes about 1 sec
103
        return _get_cpu_by_pycpuinfo()
104
105
106
@host_info_getter(name='gpus')
107
def _gpus():
108
    if not SETTINGS.HOST_INFO.INCLUDE_GPU_INFO:
109
        return
110
111
    try:
112
        xml = subprocess.check_output(['nvidia-smi', '-q', '-x']).decode()
113
    except (FileNotFoundError, OSError, subprocess.CalledProcessError):
114
        raise IgnoreHostInfo()
115
116
    gpu_info = {'gpus': []}
117
    for child in ET.fromstring(xml):
118
        if child.tag == 'driver_version':
119
            gpu_info['driver_version'] = child.text
120
        if child.tag != 'gpu':
121
            continue
122
        gpu = {
123
            'model': child.find('product_name').text,
124
            'total_memory': int(child.find('fb_memory_usage').find('total')
125
                                .text.split()[0]),
126
            'persistence_mode': (child.find('persistence_mode').text ==
127
                                 'Enabled')
128
        }
129
        gpu_info['gpus'].append(gpu)
130
131
    return gpu_info
132
133
134
@host_info_getter(name='ENV')
135
def _environment():
136
    keys_to_capture = SETTINGS.HOST_INFO.CAPTURED_ENV
137
    return {k: os.environ[k] for k in keys_to_capture if k in os.environ}
138
139
140
# ################### Get CPU Information ###############################
141
142
143
def _get_cpu_by_sysctl():
144
    os.environ['PATH'] += ':/usr/sbin'
145
    command = ["sysctl", "-n", "machdep.cpu.brand_string"]
146
    return subprocess.check_output(command).decode().strip()
147
148
149
def _get_cpu_by_proc_cpuinfo():
150
    command = ["cat", "/proc/cpuinfo"]
151
    all_info = subprocess.check_output(command).decode()
152
    model_pattern = re.compile("^\s*model name\s*:")
153
    for line in all_info.split("\n"):
154
        if model_pattern.match(line):
155
            return model_pattern.sub("", line, 1).strip()
156
157
158
def _get_cpu_by_pycpuinfo():
159
    return cpuinfo.get_cpu_info()['brand']
160