Revision fa9c0c38 kamaki/cli/utils/__init__.py

b/kamaki/cli/utils/__init__.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from sys import stdout
34
from sys import stdout, stdin
35 35
from re import compile as regex_compile
36
from time import sleep
37 36
from os import walk, path
38 37
from json import dumps
38
from pydoc import pager
39 39

  
40 40
from kamaki.cli.errors import raiseCLIError
41 41

  
......
57 57
    suggest['ansicolors']['active'] = True
58 58

  
59 59

  
60
def _print(w):
61
    """Print wrapper is used to help unittests check what is printed"""
62
    print w
63

  
64

  
65
def _write(w):
66
    """stdout.write wrapper is used to help unittests check what is printed"""
67
    stdout.write(w)
68

  
69

  
70
def _flush():
71
    """stdout.flush wrapper is used to help unittests check what is called"""
72
    stdout.flush()
73

  
74

  
75
def _readline():
76
    """raw_input wrapper is used to help unittests"""
77
    return raw_input()
78

  
79

  
80 60
def suggest_missing(miss=None, exclude=[]):
81 61
    global suggest
82 62
    sgs = dict(suggest)
......
131 111
    return new_d
132 112

  
133 113

  
134
def print_json(data):
114
def print_json(data, out=stdout):
135 115
    """Print a list or dict as json in console
136 116

  
137 117
    :param data: json-dumpable data
138
    """
139
    _print(dumps(data, indent=INDENT_TAB))
140

  
141 118

  
142
def pretty_dict(d, *args, **kwargs):
143
    print_dict(pretty_keys(d, *args, **kwargs))
119
    :param out: Input/Output stream to dump values into
120
    """
121
    out.writelines(unicode(dumps(data, indent=INDENT_TAB) + '\n'))
144 122

  
145 123

  
146 124
def print_dict(
147 125
        d,
148 126
        exclude=(), indent=0,
149
        with_enumeration=False, recursive_enumeration=False):
127
        with_enumeration=False, recursive_enumeration=False, out=stdout):
150 128
    """Pretty-print a dictionary object
151 129
    <indent>key: <non iterable item>
152 130
    <indent>key:
......
163 141
    :param recursive_enumeration: (bool) recursively enumerate iterables (does
164 142
        not enumerate 1st level keys)
165 143

  
144
    :param out: Input/Output stream to dump values into
145

  
166 146
    :raises CLIError: if preconditions fail
167 147
    """
168 148
    assert isinstance(d, dict), 'print_dict input must be a dict'
......
172 152
        k = ('%s' % k).strip()
173 153
        if k in exclude:
174 154
            continue
175
        print_str = ' ' * indent
176
        print_str += '%s.' % (i + 1) if with_enumeration else ''
177
        print_str += '%s:' % k
155
        print_str = u' ' * indent
156
        print_str += u'%s.' % (i + 1) if with_enumeration else u''
157
        print_str += u'%s:' % k
178 158
        if isinstance(v, dict):
179
            _print(print_str)
159
            out.writelines(print_str + '\n')
180 160
            print_dict(
181 161
                v, exclude, indent + INDENT_TAB,
182
                recursive_enumeration, recursive_enumeration)
162
                recursive_enumeration, recursive_enumeration, out)
183 163
        elif isinstance(v, list) or isinstance(v, tuple):
184
            _print(print_str)
164
            out.writelines(print_str + '\n')
185 165
            print_list(
186 166
                v, exclude, indent + INDENT_TAB,
187
                recursive_enumeration, recursive_enumeration)
167
                recursive_enumeration, recursive_enumeration, out)
188 168
        else:
189
            _print('%s %s' % (print_str, v))
169
            out.writelines(u'%s %s\n' % (print_str, v))
190 170

  
191 171

  
192 172
def print_list(
193 173
        l,
194 174
        exclude=(), indent=0,
195
        with_enumeration=False, recursive_enumeration=False):
175
        with_enumeration=False, recursive_enumeration=False, out=stdout):
196 176
    """Pretty-print a list of items
197 177
    <indent>key: <non iterable item>
198 178
    <indent>key:
......
209 189
    :param recursive_enumeration: (bool) recursively enumerate iterables (does
210 190
        not enumerate 1st level keys)
211 191

  
192
    :param out: Input/Output stream to dump values into
193

  
212 194
    :raises CLIError: if preconditions fail
213 195
    """
214 196
    assert isinstance(l, list) or isinstance(l, tuple), (
......
216 198
    assert indent >= 0, 'print_list indent must be >= 0'
217 199

  
218 200
    for i, item in enumerate(l):
219
        print_str = ' ' * indent
220
        print_str += '%s.' % (i + 1) if with_enumeration else ''
201
        print_str = u' ' * indent
202
        print_str += u'%s.' % (i + 1) if with_enumeration else u''
221 203
        if isinstance(item, dict):
222 204
            if with_enumeration:
223
                _print(print_str)
205
                out.writelines(print_str + '\n')
224 206
            elif i and i < len(l):
225
                _print('')
207
                out.writelines(u'\n')
226 208
            print_dict(
227 209
                item, exclude,
228 210
                indent + (INDENT_TAB if with_enumeration else 0),
229
                recursive_enumeration, recursive_enumeration)
211
                recursive_enumeration, recursive_enumeration, out)
230 212
        elif isinstance(item, list) or isinstance(item, tuple):
231 213
            if with_enumeration:
232
                _print(print_str)
214
                out.writelines(print_str + '\n')
233 215
            elif i and i < len(l):
234
                _print()
216
                out.writelines(u'\n')
235 217
            print_list(
236 218
                item, exclude, indent + INDENT_TAB,
237
                recursive_enumeration, recursive_enumeration)
219
                recursive_enumeration, recursive_enumeration, out)
238 220
        else:
239 221
            item = ('%s' % item).strip()
240 222
            if item in exclude:
241 223
                continue
242
            _print('%s%s' % (print_str, item))
243

  
244

  
245
def page_hold(index, limit, maxlen):
246
    """Check if there are results to show, and hold the page when needed
247
    :param index: (int) > 0, index of current element
248
    :param limit: (int) 0 < limit <= max, page hold if limit mod index == 0
249
    :param maxlen: (int) Don't hold if index reaches maxlen
250

  
251
    :returns: True if there are more to show, False if all results are shown
252
    """
253
    if index >= maxlen:
254
        return False
255
    if index and index % limit == 0:
256
        raw_input('(%s listed - %s more - "enter" to continue)' % (
257
            index, maxlen - index))
258
    return True
224
            out.writelines(u'%s%s\n' % (print_str, item))
259 225

  
260 226

  
261 227
def print_items(
262 228
        items, title=('id', 'name'),
263
        with_enumeration=False, with_redundancy=False,
264
        page_size=0):
229
        with_enumeration=False, with_redundancy=False, out=stdout):
265 230
    """print dict or list items in a list, using some values as title
266 231
    Objects of next level don't inherit enumeration (default: off) or titles
267 232

  
......
273 238

  
274 239
    :param with_redundancy: (boolean) values in title also appear on body
275 240

  
276
    :param page_size: (int) show results in pages of page_size items, enter to
277
        continue
241
    :param out: Input/Output stream to dump values into
278 242
    """
279 243
    if not items:
280 244
        return
281 245
    if not (isinstance(items, dict) or isinstance(items, list) or isinstance(
282 246
                items, tuple)):
283
        _print('%s' % items)
247
        out.writelines(u'%s\n' % items)
284 248
        return
285 249

  
286
    page_size = int(page_size or 0)
287
    try:
288
        page_size = page_size if page_size > 0 else len(items)
289
    except:
290
        page_size = len(items)
291
    num_of_pages = len(items) // page_size
292
    num_of_pages += 1 if len(items) % page_size else 0
293 250
    for i, item in enumerate(items):
294 251
        if with_enumeration:
295
            _write('%s. ' % (i + 1))
252
            out.write(u'%s. ' % (i + 1))
296 253
        if isinstance(item, dict):
297 254
            item = dict(item)
298 255
            title = sorted(set(title).intersection(item))
299 256
            pick = item.get if with_redundancy else item.pop
300
            header = ' '.join('%s' % pick(key) for key in title)
301
            _print(bold(header))
302
            print_dict(item, indent=INDENT_TAB)
257
            header = u' '.join(u'%s' % pick(key) for key in title)
258
            out.writelines(unicode(bold(header) + '\n'))
259
            print_dict(item, indent=INDENT_TAB, out=out)
303 260
        elif isinstance(item, list) or isinstance(item, tuple):
304
            print_list(item, indent=INDENT_TAB)
261
            print_list(item, indent=INDENT_TAB, out=out)
305 262
        else:
306
            _print(' %s' % item)
307
        page_hold(i + 1, page_size, len(items))
263
            out.writelines(u' %s\n' % item)
308 264

  
309 265

  
310 266
def format_size(size, decimal_factors=False):
......
418 374
    return terms
419 375

  
420 376

  
421
def ask_user(msg, true_resp=('y', )):
377
def ask_user(msg, true_resp=('y', ), out=stdout, user_in=stdin):
422 378
    """Print msg and read user response
423 379

  
424 380
    :param true_resp: (tuple of chars)
425 381

  
426 382
    :returns: (bool) True if reponse in true responses, False otherwise
427 383
    """
428
    _write('%s [%s/N]: ' % (msg, ', '.join(true_resp)))
429
    _flush()
430
    user_response = _readline()
431
    return user_response[0].lower() in true_resp
432

  
433

  
434
def spiner(size=None):
435
    spins = ('/', '-', '\\', '|')
436
    _write(' ')
437
    size = size or -1
438
    i = 0
439
    while size - i:
440
        _write('\b%s' % spins[i % len(spins)])
441
        _flush()
442
        i += 1
443
        sleep(0.1)
444
        yield
445
    yield
384
    yep = ', '.join(true_resp)
385
    nope = '<not %s>' % yep if 'n' in true_resp or 'N' in true_resp else 'N'
386
    out.write(u'%s [%s/%s]: ' % (msg, yep, nope))
387
    out.flush()
388
    user_response = user_in.readline()
389
    return user_response[0].lower() in [s.lower() for s in true_resp]
446 390

  
447 391

  
448 392
def get_path_size(testpath):

Also available in: Unified diff