Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

# utile.py - Collection of useful functions 

# Copyright (C) 2013 Marwan Alsabbagh 

# license: BSD, see LICENSE for more details. 

 

__version__ = '0.3.dev' 

 

import time 

import re 

import os 

import os.path 

import sys 

import itertools 

import logging.config 

from timeit import default_timer as timer 

from functools import wraps 

from shutil import rmtree 

from hashlib import sha256 

from inspect import getargspec 

from subprocess import check_output, check_call, Popen, PIPE 

from tempfile import mkdtemp 

from contextlib import contextmanager 

from fcntl import flock, LOCK_EX, LOCK_NB 

from datetime import timedelta, datetime 

from importlib import import_module 

from argparse import ( 

    ArgumentParser, ArgumentDefaultsHelpFormatter, RawDescriptionHelpFormatter) 

 

# Alias for easier importing 

now = datetime.now 

 

 

def resolve(name): 

    item, names = None, name.split('.') 

    for i, subname in enumerate(names): 

        try: 

            item = getattr(item, subname) 

        except AttributeError: 

            item = import_module('.'.join(names[:i + 1])) 

    return item 

 

 

def safe_import(name, default=None): 

    try: 

        return resolve(name) 

    except ImportError: 

        return default 

 

 

etree = safe_import('lxml.etree') 

AES = safe_import('Crypto.Cipher.AES') 

bunch_or_dict = safe_import('bunch.Bunch', dict) 

 

 

def dir_dict(obj, default=None): 

    names = [i for i in dir(obj) if not i.startswith('_')] 

    return {i: getattr(obj, i, default) for i in names} 

 

 

def pretty_xml(xml): 

    enforce(etree, 'lxml is not installed.') 

    root = etree.fromstring(xml, etree.XMLParser(remove_blank_text=True)) 

    return etree.tostring(root, pretty_print=True) 

 

 

def element_to_dict(elem, return_tuple=False): 

    children = bunch_or_dict(element_to_dict(i, True) for i in elem) 

    if return_tuple: 

        return elem.tag, children or elem.text 

    else: 

        return children 

 

 

def xml_to_dict(xml, *args, **kwargs): 

    enforce(etree, 'lxml is not installed.') 

    root = etree.fromstring(xml, etree.XMLParser(*args, **kwargs)) 

    return element_to_dict(root) 

 

 

def git_version(version): 

    git_details = '' 

    if which('git'): 

        cmd = ['git', 'describe'] 

        git_details = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()[0] 

    if 'dev' not in version: 

        return version 

    elif git_details: 

        return version + git_details.split('-')[1] 

    elif os.path.exists('PKG-INFO'): 

        info = open('PKG-INFO').read() 

        return re.findall(r'^Version: (.*)$', info, re.MULTILINE)[0] 

    else: 

        return version 

 

 

def save_args(f): 

    @wraps(f) 

    def wrapper(self, *args, **kwargs): 

        names = getargspec(f).args[1:] 

        defaults = zip(reversed(names), reversed(getargspec(f).defaults)) 

        items = zip(names, args) + kwargs.items() + defaults 

        for k, v in items: 

            if not hasattr(self, k): 

                setattr(self, k, v) 

        return f(self, *args, **kwargs) 

    return wrapper 

 

 

def flatten(data): 

    return list(itertools.chain.from_iterable(data)) 

 

 

def process_name(pid=None): 

    pid = pid or os.getpid() 

    cmdline = open('/proc/%s/cmdline' % pid).read() 

    return cmdline.strip('\x00').split('\x00') 

 

 

def mac_address(interface='eth0'): 

    output = check_output(['ifconfig', interface]) 

    return re.search(r'HWaddr ([\w:]+)', output).group(1) 

 

 

@contextmanager 

def TemporaryDirectory(suffix='', prefix='tmp', dir=None): 

    path = mkdtemp(suffix, prefix, dir) 

    try: 

        yield path 

    finally: 

        rmtree(path) 

 

 

@contextmanager 

def file_lock(path): 

    try: 

        f = open(path, 'w') 

        flock(f, LOCK_EX | LOCK_NB) 

    except IOError: 

        raise IOError('Could not lock %r' % path) 

    try: 

        yield 

    finally: 

        f.close() 

        os.remove(path) 

 

 

def shell_quote(s): 

    return "'" + s.replace("'", "'\"'\"'") + "'" 

 

 

def _cipher(key): 

    enforce(AES, 'pycrypto is not installed.') 

    return AES.new(sha256(key).digest(), AES.MODE_CFB, '\x00' * AES.block_size) 

 

 

def encrypt(key, data): 

    return _cipher(key).encrypt(data) 

 

 

def decrypt(key, data): 

    return _cipher(key).decrypt(data) 

 

 

class EnforcementError(Exception): 

    pass 

 

 

def enforce(rule, msg, exception=EnforcementError): 

    if not rule: 

        raise exception(msg) 

 

 

def enforce_clean_exit(func): 

    @wraps(func) 

    def wrapper(*args, **kwds): 

        try: 

            return func(*args, **kwds) 

        except EnforcementError as err: 

            sys.exit('ERROR: %s' % err) 

    return wrapper 

 

 

def which(cmd): 

    os_paths = os.environ['PATH'].split(os.pathsep) 

    cmd_paths = [os.path.join(i, cmd) for i in os_paths] 

    return [i for i in cmd_paths if os.path.exists(i)] 

 

 

def commands_required(commands): 

    missing = [cmd for cmd in commands.split() if not which(cmd)] 

    enforce(not missing, '%r command(s) not found' % ' '.join(missing)) 

 

 

def shell(cmd=None, msg=None, caller=check_call, strict=False, **kwargs): 

    msg = msg if msg else cmd 

    kwargs.setdefault('shell', True) 

    if kwargs['shell']: 

        kwargs.setdefault('executable', '/bin/bash') 

    if strict: 

        if not kwargs['shell'] or not isinstance(cmd, basestring): 

            msg = 'strict can only be used when shell=True and cmd is a string' 

            raise ValueError(msg) 

        cmd = 'set -e;' + cmd 

    print(' {} '.format(msg).center(60, '-')) 

    start = timer() 

    returncode = caller(cmd, **kwargs) 

    print('duration: %s' % timedelta(seconds=timer() - start)) 

    return returncode 

 

 

class TimeoutError(Exception): 

    pass 

 

 

def wait(check, timeout=None, delay=0.1): 

    start = timer() 

    while not check(): 

        duration = timer() - start 

        if timeout and duration > timeout: 

            raise TimeoutError('waited for %0.3fs' % duration) 

        time.sleep(delay) 

 

 

class ArgDefaultRawHelpFormatter( 

        ArgumentDefaultsHelpFormatter, RawDescriptionHelpFormatter): 

    """Help message formatter which adds default values to argument help and 

    which retains any formatting in descriptions. 

    """ 

 

 

class Arg(object): 

    def __init__(self, *args, **kwargs): 

        self.args = args 

        self.kwargs = kwargs 

 

 

def parse_args(description, *args, **kwargs): 

    kwargs['description'] = description 

    kwargs.setdefault('formatter_class', ArgDefaultRawHelpFormatter) 

    parser = ArgumentParser(**kwargs) 

    for i in args: 

        parser.add_argument(*i.args, **i.kwargs) 

    return bunch_or_dict(parser.parse_args().__dict__)