Fix spelling mistake (ident-->indent)
[kamaki] / kamaki / cli / utils.py
index 4ee07b0..6499721 100644 (file)
 from sys import stdout, stdin
 from re import compile as regex_compile
 from time import sleep
+from os import walk, path
+from json import dumps
 
 from kamaki.cli.errors import raiseCLIError
 
+
+INDENT_TAB = 4
+
+
+suggest = dict(ansicolors=dict(
+        active=False,
+        url='#install-ansicolors-progress',
+        description='Add colors to console responses'))
+
 try:
     from colors import magenta, red, yellow, bold
 except ImportError:
@@ -44,6 +55,32 @@ except ImportError:
     def dummy(val):
         return val
     red = yellow = magenta = bold = dummy
+    #from kamaki.cli import _colors
+    #if _colors.lower() == 'on':
+    suggest['ansicolors']['active'] = True
+
+try:
+    from progress.bar import ShadyBar
+except ImportError:
+    suggest['progress']['active'] = True
+
+
+def suggest_missing(miss=None, exclude=[]):
+    global suggest
+    sgs = dict(suggest)
+    for exc in exclude:
+        try:
+            sgs.pop(exc)
+        except KeyError:
+            pass
+    kamaki_docs = 'http://www.synnefo.org/docs/kamaki/latest'
+    for k, v in (miss, sgs[miss]) if miss else sgs.items():
+        if v['active'] and stdout.isatty():
+            print('Suggestion: for better user experience install %s' % k)
+            print('\t%s' % v['description'])
+            print('\tIt is easy, here are the instructions:')
+            print('\t%s/installation.html%s' % (kamaki_docs, v['url']))
+            print('')
 
 
 def remove_colors():
@@ -71,126 +108,117 @@ def pretty_keys(d, delim='_', recurcive=False):
     return new_d
 
 
+def print_json(data):
+    """Print a list or dict as json in console
+
+    :param data: json-dumpable data
+    """
+    print(dumps(data, indent=INDENT_TAB))
+
+
+def pretty_dict(d, *args, **kwargs):
+    print_dict(pretty_keys(d, *args, **kwargs))
+
+
 def print_dict(
-        d, exclude=(), ident=0,
+        d,
+        exclude=(), indent=0,
         with_enumeration=False, recursive_enumeration=False):
-    """
-    Pretty-print a dictionary object
+    """Pretty-print a dictionary object
+    <indent>key: <non iterable item>
+    <indent>key:
+    <indent + INDENT_TAB><pretty-print iterable>
 
-    :param d: (dict) the input
+    :param d: (dict)
 
-    :param excelude: (set or list) keys to exclude from printing
+    :param exclude: (iterable of strings) keys to exclude from printing
 
-    :param ident: (int) initial indentation (recursive)
+    :param indent: (int) initial indentation (recursive)
 
-    :param with_enumeration: (bool) enumerate each 1st level key if true
+    :param with_enumeration: (bool) enumerate 1st-level keys
 
-    :recursive_enumeration: (bool) recursively enumerate dicts and lists of
-        2nd level or deeper
+    :param recursive_enumeration: (bool) recursively enumerate iterables (does
+        not enumerate 1st level keys)
 
-    :raises CLIError: (TypeError wrapper) non-dict input
+    :raises CLIError: if preconditions fail
     """
-    if not isinstance(d, dict):
-        raiseCLIError(TypeError('Cannot dict_print a non-dict object'))
+    assert isinstance(d, dict), 'print_dict input must be a dict'
+    assert indent >= 0, 'print_dict indent must be >= 0'
 
-    if d:
-        margin = max(len(('%s' % key).strip()) for key in d.keys() if (
-            key not in exclude))
-
-    counter = 1
-    for key, val in sorted(d.items()):
-        if key in exclude:
+    for i, (k, v) in enumerate(d.items()):
+        k = ('%s' % k).strip()
+        if k in exclude:
             continue
-        print_str = ''
-        if with_enumeration:
-            print_str = '%s. ' % counter
-            counter += 1
-        print_str = '%s%s' % (' ' * (ident - len(print_str)), print_str)
-        print_str += ('%s' % key).strip()
-        print_str += ' ' * (margin - len(unicode(key).strip()))
-        print_str += ': '
-        if isinstance(val, dict):
-            print(print_str)
+        print_str = ' ' * indent
+        print_str += '%s.' % (i + 1) if with_enumeration else ''
+        print_str += '%s:' % k
+        if isinstance(v, dict):
+            print print_str
             print_dict(
-                val,
-                exclude=exclude,
-                ident=margin + ident,
-                with_enumeration=recursive_enumeration,
-                recursive_enumeration=recursive_enumeration)
-        elif isinstance(val, list):
-            print(print_str)
+                v, exclude, indent + INDENT_TAB,
+                recursive_enumeration, recursive_enumeration)
+        elif isinstance(v, list) or isinstance(v, tuple):
+            print print_str
             print_list(
-                val,
-                exclude=exclude,
-                ident=margin + ident,
-                with_enumeration=recursive_enumeration,
-                recursive_enumeration=recursive_enumeration)
+                v, exclude, indent + INDENT_TAB,
+                recursive_enumeration, recursive_enumeration)
         else:
-            print print_str + ' ' + unicode(val).strip()
+            print '%s %s' % (print_str, v)
 
 
 def print_list(
-        l, exclude=(), ident=0,
+        l,
+        exclude=(), indent=0,
         with_enumeration=False, recursive_enumeration=False):
-    """
-    Pretty-print a list object
+    """Pretty-print a list of items
+    <indent>key: <non iterable item>
+    <indent>key:
+    <indent + INDENT_TAB><pretty-print iterable>
 
-    :param l: (list) the input
+    :param l: (list)
 
-    :param excelude: (object - anytype) values to exclude from printing
+    :param exclude: (iterable of strings) items to exclude from printing
 
-    :param ident: (int) initial indentation (recursive)
+    :param indent: (int) initial indentation (recursive)
 
-    :param with_enumeration: (bool) enumerate each 1st level value if true
+    :param with_enumeration: (bool) enumerate 1st-level items
 
-    :recursive_enumeration: (bool) recursively enumerate dicts and lists of
-        2nd level or deeper
+    :param recursive_enumeration: (bool) recursively enumerate iterables (does
+        not enumerate 1st level keys)
 
-    :raises CLIError: (TypeError wrapper) non-list input
+    :raises CLIError: if preconditions fail
     """
-    if not isinstance(l, list):
-        raiseCLIError(TypeError('Cannot list_print a non-list object'))
-
-    if l:
-        try:
-            margin = max(len(('%s' % item).strip()) for item in l if not (
-                isinstance(item, dict) or
-                isinstance(item, list) or
-                item in exclude))
-        except ValueError:
-            margin = (2 + len(unicode(len(l)))) if enumerate else 1
-
-    counter = 1
-    prefix = ''
-    for item in sorted(l):
-        if item in exclude:
-            continue
-        elif with_enumeration:
-            prefix = '%s. ' % counter
-            counter += 1
-            prefix = '%s%s' % (' ' * (ident - len(prefix)), prefix)
-        else:
-            prefix = ' ' * ident
+    assert isinstance(l, list) or isinstance(l, tuple), (
+        'print_list prinbts a list or tuple')
+    assert indent >= 0, 'print_list indent must be >= 0'
+
+    counter = 0
+    for i, item in enumerate(l):
+        print_str = ' ' * indent
+        print_str += '%s.' % (i + 1) if with_enumeration else ''
         if isinstance(item, dict):
             if with_enumeration:
-                print(prefix)
+                print print_str
+            elif counter and counter < len(l):
+                print
             print_dict(
-                item,
-                exclude=exclude,
-                ident=margin + ident,
-                with_enumeration=recursive_enumeration,
-                recursive_enumeration=recursive_enumeration)
-        elif isinstance(item, list):
+                item, exclude,
+                indent + (INDENT_TAB if with_enumeration else 0),
+                recursive_enumeration, recursive_enumeration)
+        elif isinstance(item, list) or isinstance(item, tuple):
             if with_enumeration:
-                print(prefix)
+                print print_str
+            elif counter and counter < len(l):
+                print
             print_list(
-                item,
-                exclude=exclude,
-                ident=margin + ident,
-                with_enumeration=recursive_enumeration,
-                recursive_enumeration=recursive_enumeration)
+                item, exclude, indent + INDENT_TAB,
+                recursive_enumeration, recursive_enumeration)
         else:
-            print('%s%s' % (prefix, item))
+            item = ('%s' % item).strip()
+            if item in exclude:
+                continue
+            print '%s%s' % (print_str, item)
+        counter += 1
 
 
 def page_hold(index, limit, maxlen):
@@ -222,9 +250,13 @@ def print_items(
     Objects of next level don't inherit enumeration (default: off) or titles
 
     :param items: (list) items are lists or dict
+
     :param title: (tuple) keys to use their values as title
+
     :param with_enumeration: (boolean) enumerate items (order id on title)
+
     :param with_redundancy: (boolean) values in title also appear on body
+
     :param page_size: (int) show results in pages of page_size items, enter to
         continue
     """
@@ -242,14 +274,14 @@ def print_items(
         if isinstance(item, dict):
             title = sorted(set(title).intersection(item.keys()))
             if with_redundancy:
-                header = ' '.join(unicode(item[key]) for key in title)
+                header = ' '.join('%s' % item[key] for key in title)
             else:
-                header = ' '.join(unicode(item.pop(key)) for key in title)
+                header = ' '.join('%s' % item.pop(key) for key in title)
             print(bold(header))
         if isinstance(item, dict):
-            print_dict(item, ident=1)
+            print_dict(item, indent=INDENT_TAB)
         elif isinstance(item, list):
-            print_list(item, ident=1)
+            print_list(item, indent=INDENT_TAB)
         else:
             print(' %s' % item)
         page_hold(i + 1, page_size, len(items))
@@ -305,7 +337,7 @@ def dict2file(d, f, depth=0):
             f.write('\n')
             list2file(v, f, depth + 1)
         else:
-            f.write(' %s\n' % unicode(v))
+            f.write(' %s\n' % v)
 
 
 def list2file(l, f, depth=1):
@@ -315,7 +347,7 @@ def list2file(l, f, depth=1):
         elif isinstance(item, list):
             list2file(item, f, depth + 1)
         else:
-            f.write('%s%s\n' % ('\t' * depth, unicode(item)))
+            f.write('%s%s\n' % ('\t' * depth, item))
 
 # Split input auxiliary
 
@@ -381,17 +413,17 @@ def split_input(line):
     return terms
 
 
-def ask_user(msg, true_resp=['Y', 'y']):
+def ask_user(msg, true_resp=('y', )):
     """Print msg and read user response
 
     :param true_resp: (tuple of chars)
 
     :returns: (bool) True if reponse in true responses, False otherwise
     """
-    stdout.write('%s (%s or enter for yes):' % (msg, ', '.join(true_resp)))
+    stdout.write('%s [%s/N]: ' % (msg, ', '.join(true_resp)))
     stdout.flush()
     user_response = stdin.readline()
-    return user_response[0] in true_resp + ['\n']
+    return user_response[0].lower() in true_resp
 
 
 def spiner(size=None):
@@ -434,3 +466,21 @@ if __name__ == '__main__':
         print('%s. Split this: (%s)' % (i + 1, example))
         ret = old_split_input(example)
         print('\t(%s) of size %s' % (ret, len(ret)))
+
+
+def get_path_size(testpath):
+    if path.isfile(testpath):
+        return path.getsize(testpath)
+    total_size = 0
+    for top, dirs, files in walk(path.abspath(testpath)):
+        for f in files:
+            f = path.join(top, f)
+            if path.isfile(f):
+                total_size += path.getsize(f)
+    return total_size
+
+
+def remove_from_items(list_of_dicts, key_to_remove):
+    for item in list_of_dicts:
+        assert isinstance(item, dict), 'Item %s not a dict' % item
+        item.pop(key_to_remove, None)