Repository: softlayer/softlayer-python
Pull request #1189 (2019-11-18 18:40:01)

Issues771 - Fixing windows Unicode Errors
`slcli image list --public` has a unicode character that causes an exception in Windows shells for some reason. 

image id=1288413 is the problematic one. To reproduce on master

```bash
$ ./slcli image detail 1288413
An unexpected error has occured:
Traceback (most recent call last):
  File "C:\Users\allmi\Source\softlayer-python\SoftLayer\CLI\core.py", line 182, in main
    cli.main(**kwargs)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 717, in main
    rv = self.invoke(ctx)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "C:\Users\allmi\Source\softlayer-python\SoftLayer\CLI\image\detail.py", line 63, in cli
    env.fout(table)
  File "C:\Users\allmi\Source\softlayer-python\SoftLayer\CLI\environment.py", line 56, in fout
    self.out(self.fmt(output), newline=newline)
  File "C:\Users\allmi\Source\softlayer-python\SoftLayer\CLI\environment.py", line 43, in out
    click.echo(output, nl=newline)
  File "C:\Users\allmi\Source\py36\lib\site-packages\click\utils.py", line 260, in echo
    file.write(message)
  File "C:\Users\allmi\Source\py36\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u3010' in position 140: character maps to <undefined>

Feel free to report this error as it is likely a bug:
    https://github.com/softlayer/softlayer-python/issues
The following snippet should be able to reproduce the error
(py36)


$ ./slcli image list | grep 1288413
(py36)

```

When fixed should look like this:

```
$ ./slcli image detail 1288413
UnicodeEncodeError detected, printing as JSON.
{
    "id": 1288413,
    "global_identifier": "fd16452d-1a03-48b7-ae52-6b1d15632051",
    "name": "\u3010CEDEC\u3011Photon Handson image",
    "status": null,
    "active_transaction": null,
    "account": 363380,
    "visibility": "PUBLIC",
    "type": "SYSTEM",
    "flex": false,
    "note": "Photon\u30a4\u30e1\u30fc\u30b8",
    "created": "2016-08-19T01:01:52-07:00",
    "disk_space": 32203940352,
    "datacenters": "tok02,wdc01"
}

$ ./slcli image list | grep 1288413
        "id": 1288413,
(py36)

```





Patch:
diff --git a/SoftLayer/CLI/environment.py b/SoftLayer/CLI/environment.py
index cb914fef..c73bc638 100644
--- a/SoftLayer/CLI/environment.py
+++ b/SoftLayer/CLI/environment.py
@@ -46,14 +46,20 @@ def err(self, output, newline=True):
         """Outputs an error string to the console (stderr)."""
         click.echo(output, nl=newline, err=True)
 
-    def fmt(self, output):
+    def fmt(self, output, fmt=None):
         """Format output based on current the environment format."""
-        return formatting.format_output(output, fmt=self.format)
+        if fmt is None:
+            fmt = self.format
+        return formatting.format_output(output, fmt)
 
     def fout(self, output, newline=True):
         """Format the input and output to the console (stdout)."""
         if output is not None:
-            self.out(self.fmt(output), newline=newline)
+            try:
+                self.out(self.fmt(output), newline=newline)
+            except UnicodeEncodeError:
+                # If we hit an undecodeable entry, just try outputting as json.
+                self.out(self.fmt(output, 'json'), newline=newline)
 
     def input(self, prompt, default=None, show_default=True):
         """Provide a command prompt."""


Test patch:
diff --git a/tests/CLI/environment_tests.py b/tests/CLI/environment_tests.py
index 5d04e153..fa90ba1d 100644
--- a/tests/CLI/environment_tests.py
+++ b/tests/CLI/environment_tests.py
@@ -62,3 +62,14 @@ def test_resolve_alias(self):
 
         r = self.env.resolve_alias('realname')
         self.assertEqual(r, 'realname')
+
+    @mock.patch('click.echo')
+    def test_print_unicode(self, echo):
+        output = "\u3010TEST\u3011 image"
+        # https://docs.python.org/3.6/library/exceptions.html#UnicodeError
+        echo.side_effect = [
+            UnicodeEncodeError('utf8', output, 0, 1, "Test Exception"),
+            output
+        ]
+        self.env.fout(output)
+        self.assertEqual(2, echo.call_count)