Repository: Flexget/Flexget

Issue:
Unicode issues with exec plugin
Same as https://github.com/Flexget/Flexget/issues/1269, but with exec plugin

It was working well recently, but now if the given path contains unicode (e. g. "ñ") characters it crashes.

Running flexget version 2.1.23 installed using pip, on Windows 8.1 x64 + Python 2.7.12

Relevant snippet of crash report (crash_report.2016.07.25.115814396000.log): 

```
2016-07-25 11:58 CRITICAL task          remove-folders  BUG: Unhandled error in plugin exec: 'ascii' codec can't encode character u'\xf1' in position 124: ordinal not in range(128)
Traceback (most recent call last):
  File "c:\python27\lib\site-packages\flexget\task.py", line 444, in __run_plugin
    return method(*args, **kwargs)
  File "c:\python27\lib\site-packages\flexget\event.py", line 23, in __call__
    return self.func(*args, **kwargs)
  File "c:\python27\lib\site-packages\flexget\plugins\output\exec.py", line 192, in phase_handler
    self.execute(task, 'on_' + phase, config)
  File "c:\python27\lib\site-packages\flexget\plugins\output\exec.py", line 163, in execute
    if (self.execute_cmd(cmd, allow_background, config['encoding']) != 0 and
  File "c:\python27\lib\site-packages\flexget\plugins\output\exec.py", line 109, in execute_cmd
    stderr=subprocess.STDOUT, close_fds=False)
  File "c:\python27\lib\subprocess.py", line 711, in __init__
    errread, errwrite)
  File "c:\python27\lib\subprocess.py", line 929, in _execute_child
    args = '{} /c "{}"'.format (comspec, args)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf1' in position 124: ordinal not in range(128)
```



Discussion:

Resolving patch:
diff --git a/flexget/plugins/output/exec.py b/flexget/plugins/output/exec.py
--- a/flexget/plugins/output/exec.py
+++ b/flexget/plugins/output/exec.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals, division, absolute_import
 from builtins import *  # pylint: disable=unused-import, redefined-builtin
 from past.builtins import basestring
+from future.utils import text_to_native_str
 
 import logging
 import subprocess
@@ -10,7 +11,7 @@
 from flexget.event import event
 from flexget.config_schema import one_or_more
 from flexget.utils.template import render_from_entry, render_from_task, RenderError
-from flexget.utils.tools import io_encoding, native_str_to_text
+from flexget.utils.tools import io_encoding
 
 log = logging.getLogger('exec')
 
@@ -103,17 +104,16 @@ def prepare_config(self, config):
         return config
 
     def execute_cmd(self, cmd, allow_background, encoding):
-        log.verbose('Executing: %s' % cmd)
-        # if PY2: cmd = cmd.encode(encoding) ?
-        p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT, close_fds=False)
+        log.verbose('Executing: %s', cmd)
+        p = subprocess.Popen(text_to_native_str(cmd, encoding=io_encoding), shell=True, stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=False)
         if not allow_background:
-            (r, w) = (p.stdout, p.stdin)
-            response = native_str_to_text(r.read(), encoding=encoding, errors='replace')
+            r, w = (p.stdout, p.stdin)
+            response = r.read().decode(io_encoding)
             r.close()
             w.close()
             if response:
-                log.info('Stdout: %s' % response)
+                log.info('Stdout: %s', response.rstrip())  # rstrip to get rid of newlines
         return p.wait()
 
     def execute(self, task, phase_name, config):


Test patch: