Repository: AnalogJ/lexicon

Issue:
Memset provider: TypeError: string indices must be integers
Hi,

When using the Memset provider with the default table formatting I get this error:

```bash
$ lexicon memset create example.com TXT --name _acme-challenge.example.com --content BLAH --ttl 300
Traceback (most recent call last):
  File "/usr/local/bin/lexicon", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/dist-packages/lexicon/__main__.py", line 133, in main
    handle_output(results, parsed_args.output)
  File "/usr/local/lib/python2.7/dist-packages/lexicon/__main__.py", line 109, in handle_output
    table = generate_table_result(logger, results, output_type == 'TABLE-NO-HEADER')
  File "/usr/local/lib/python2.7/dist-packages/lexicon/__main__.py", line 75, in generate_table_result
    array = [[row['id'], row['type'], row['name'], row['content'], row['ttl']] for row in output]
TypeError: string indices must be integers
```

I think this is because `output` is a string not an array - when I added `print output` I got a string like `969f9caabe19859c11249333dd80aa15`.

When I use `--output JSON` I get the same ID plus quotes:

```bash
$ lexicon memset create example.com TXT --name _acme-challenge.example.com --content BLAH --ttl 300 --output JSON
"969f9caabe19859c11249333dd80aa15"
```

I know Memset's not public so if you need any help to test it just let me know. For now I'll work around it with `--output QUIET` since I don't really care about the output here.

Thanks!
Dave


Discussion:
Hmm, thats odd. I'm a bit busy with `lexicon` release issues right now, but I'll see if I can pick this up sooner rather than later. 


Resolving patch:
diff --git a/lexicon/cli.py b/lexicon/cli.py
--- a/lexicon/cli.py
+++ b/lexicon/cli.py
@@ -14,12 +14,10 @@
 logger = logging.getLogger(__name__)  # pylint: disable=C0103
 
 
-def generate_table_result(lexicon_logger, output=None, without_header=None):
-    """Convert returned JSON into a nice table for command line usage"""
-    try:
-        _ = (entry for entry in output)
-    except TypeError:
-        lexicon_logger.debug('Command output is not iterable, and then cannot '
+def generate_list_table_result(lexicon_logger, output=None, without_header=None):
+    """Convert returned data from list actions into a nice table for command line usage"""
+    if not isinstance(output, list):
+        lexicon_logger.debug('Command output is not a list, and then cannot '
                              'be printed with --quiet parameter not enabled.')
         return None
 
@@ -58,26 +56,43 @@ def generate_table_result(lexicon_logger, output=None, without_header=None):
         table.append(' '.join(row_list))
 
     # Return table
-    return '\n'.join(table)
+    return os.linesep.join(table)
 
 
-def handle_output(results, output_type):
+def generate_table_results(output=None, without_header=None):
+    """Convert returned data from non-list actions into a nice table for command line usage"""
+    array = []
+    str_output = str(output)
+
+    if not without_header:
+        array.append('RESULT')
+        array.append('-' * max(6, len(str_output)))
+
+    array.append(str_output)
+    return os.linesep.join(array)
+
+
+def handle_output(results, output_type, action):
     """Print the relevant output for given output_type"""
-    if not output_type == 'QUIET':
-        if not output_type == 'JSON':
-            table = generate_table_result(
+    if output_type == 'QUIET':
+        return
+
+    if not output_type == 'JSON':
+        if action == 'list':
+            table = generate_list_table_result(
                 logger, results, output_type == 'TABLE-NO-HEADER')
-            if table:
-                print(table)
         else:
-            try:
-                _ = (entry for entry in results)
-                json_str = json.dumps(results)
-                if json_str:
-                    print(json_str)
-            except TypeError:
-                logger.debug('Output is not a JSON, and then cannot '
-                             'be printed with --output=JSON parameter.')
+            table = generate_table_results(results, output_type == 'TABLE-NO-HEADER')
+        if table:
+            print(table)
+    else:
+        try:
+            json_str = json.dumps(results)
+            if json_str:
+                print(json_str)
+        except TypeError:
+            logger.debug('Output is not JSON serializable, and then cannot '
+                         'be printed with --output=JSON parameter.')
 
 
 def main():
@@ -101,7 +116,7 @@ def main():
 
     results = client.execute()
 
-    handle_output(results, parsed_args.output)
+    handle_output(results, parsed_args.output, config.resolve('lexicon:action'))
 
 
 if __name__ == '__main__':


Test patch:
diff --git a/tests/test_output.py b/tests/test_output.py
--- a/tests/test_output.py
+++ b/tests/test_output.py
@@ -1,12 +1,9 @@
+""" Ensure that stdout corresponds to the given reference output """
 from __future__ import absolute_import
-import importlib
 import json
 import logging
-import sys
-from types import ModuleType
 
 from lexicon import cli
-from lexicon.providers.base import Provider as BaseProvider
 
 
 logger = logging.getLogger(__name__)
@@ -17,8 +14,6 @@
         'content': 'fake2', 'ttl': 3600}
 ]
 
-# Ensure that stdout corresponds to the given reference output
-
 
 def assert_correct_output(capsys, expected_output_lines):
     out, _ = capsys.readouterr()
@@ -33,7 +28,7 @@ def test_output_function_outputs_json_as_table(capsys):
         'fake2-id TXT  fake2.example.com fake2   3600',
     ]
 
-    cli.handle_output(data, 'TABLE')
+    cli.handle_output(data, 'TABLE', 'list')
     assert_correct_output(capsys, expected_output_lines)
 
 
@@ -43,12 +38,12 @@ def test_output_function_outputs_json_as_table_with_no_header(capsys):
         'fake2-id TXT fake2.example.com fake2 3600',
     ]
 
-    cli.handle_output(data, 'TABLE-NO-HEADER')
+    cli.handle_output(data, 'TABLE-NO-HEADER', 'list')
     assert_correct_output(capsys, expected_output_lines)
 
 
 def test_output_function_outputs_json_as_json_string(capsys):
-    cli.handle_output(data, 'JSON')
+    cli.handle_output(data, 'JSON', 'list')
 
     out, _ = capsys.readouterr()
     json_data = json.loads(out)
@@ -59,18 +54,18 @@ def test_output_function_outputs_json_as_json_string(capsys):
 def test_output_function_output_nothing_when_quiet(capsys):
     expected_output_lines = []
 
-    cli.handle_output(data, 'QUIET')
+    cli.handle_output(data, 'QUIET', 'list')
     assert_correct_output(capsys, expected_output_lines)
 
 
-def test_output_function_outputs_nothing_with_not_a_json_data(capsys):
+def test_output_function_outputs_nothing_with_not_a_json_serializable(capsys):
     expected_output_lines = []
 
-    cli.handle_output(True, 'TABLE')
+    cli.handle_output(object(), 'TABLE', 'list')
     assert_correct_output(capsys, expected_output_lines)
 
-    cli.handle_output(True, 'TABLE-NO-HEADER')
+    cli.handle_output(object(), 'TABLE-NO-HEADER', 'list')
     assert_correct_output(capsys, expected_output_lines)
 
-    cli.handle_output(True, 'JSON')
+    cli.handle_output(object(), 'JSON', 'list')
     assert_correct_output(capsys, expected_output_lines)