Hi,

The lyxpak.py uses `os.popen`, which is deprecated since Python-2.6.
Furthermore, it causes the script to fail for files with quotes in their
name, e.g., quote"quote.lyx. The attached patch fixes this issue by
switching to use the module subprocess.

I made sure that it is compatible with Python-2.6, as the "Additional
Software" page on the LyX site lists this as the minimal supported version.
If support for 2.6 is not really needed, the patch could be simpler.

Thanks,

Guy
From 77525e65d15419b06abe4299f601250a6b5965a8 Mon Sep 17 00:00:00 2001
From: Guy Rutenberg <guyrutenb...@gmail.com>
Date: Sat, 11 Jul 2015 18:52:14 +0300
Subject: [PATCH] lyxpak.py: Use `subprocess` for spawning scripts.

`os.popen` is deprecated since Python-2.6. Also, this fixes the handling
of files with quotes in their names. The patch maintains compatability
with Python-2.6 by including its own version of `check_output` if it
isn't available from `subprocess`.
---
 lib/scripts/lyxpak.py |   65 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git a/lib/scripts/lyxpak.py b/lib/scripts/lyxpak.py
index 9a56bf0..76875d3 100755
--- a/lib/scripts/lyxpak.py
+++ b/lib/scripts/lyxpak.py
@@ -20,6 +20,46 @@ if sys.version_info < (2, 4, 0):
     from sets import Set as set
 from getopt import getopt
 from cStringIO import StringIO
+import subprocess
+
+
+
+# Copied almost verbatim from Python 2.7 sources, the `output` keyword argument
+# for CalledPRocessError was removed because it doesn't exists in Python 2.6
+def check_output_backport(*popenargs, **kwargs):
+    r"""Run command with arguments and return its output as a byte string.
+
+    If the exit code was non-zero it raises a CalledProcessError.  The
+    CalledProcessError object will have the return code in the returncode
+    attribute and output in the output attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    >>> check_output(["ls", "-l", "/dev/null"])
+    'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
+
+    The stdout argument is not allowed as it is used internally.
+    To capture standard error in the result, use stderr=STDOUT.
+
+    >>> check_output(["/bin/sh", "-c",
+    ...               "ls -l non_existent_file ; exit 0"],
+    ...              stderr=STDOUT)
+    'ls: non_existent_file: No such file or directory\n'
+    """
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it will be overridden.')
+    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise subprocess.CalledProcessError(retcode, cmd)
+    return output
+# If we use a recent version of Python (>=2.7) use the library's check_output,
+# otherwise use the backported one
+check_output = getattr(subprocess, 'check_output', check_output_backport)
 
 running_on_windows = (os.name == 'nt')
 
@@ -69,13 +109,6 @@ def gzopen(file, mode):
     return open(unicode(file, 'utf-8'), mode)
 
 
-def run_cmd(cmd):
-    handle = os.popen(cmd, 'r')
-    cmd_stdout = handle.read()
-    cmd_status = handle.close()
-    return cmd_status, cmd_stdout
-
-
 def find_exe(candidates, extlist, path):
     for prog in candidates:
         for directory in path:
@@ -107,13 +140,16 @@ def gather_files(curfile, incfiles, lyx2lyx):
             tmp.close()
             copyfile(unicode(curfile, 'utf-8'), tmp.name)
             lyx2lyx_cmd = 'python "%s" "%s"' % (lyx2lyx, tmp.name)
-            l2l_status, l2l_stdout = run_cmd(lyx2lyx_cmd)
+            try:
+                l2l_stdout = check_output(['python', lyx2lyx, tmp.name])
+            except subprocess.CalledProcessError:
+                error('%s failed to convert "%s"' % (lyx2lyx, curfile))
             os.unlink(tmp.name)
         else:
-            lyx2lyx_cmd = 'python "%s" "%s"' % (lyx2lyx, curfile)
-            l2l_status, l2l_stdout = run_cmd(lyx2lyx_cmd)
-        if l2l_status != None:
-            error('%s failed to convert "%s"' % (lyx2lyx, curfile))
+            try:
+                l2l_stdout = check_output(['python', lyx2lyx, curfile])
+            except subprocess.CalledProcessError:
+                error('%s failed to convert "%s"' % (lyx2lyx, curfile))
         if l2l_stdout.startswith("\x1f\x8b"):
             l2l_stdout = gzip.GzipFile("", "r", 0, StringIO(l2l_stdout)).read()
         lines = l2l_stdout.splitlines()
@@ -222,8 +258,9 @@ def find_lyx2lyx(progloc, path):
     lyx_exe, full_path = find_exe(["lyxc", "lyx"], extlist, path)
     if lyx_exe == None:
         error('Cannot find the LyX executable in the path.')
-    cmd_status, cmd_stdout = run_cmd("%s -version 2>&1" % lyx_exe)
-    if cmd_status != None:
+    try:
+        cmd_stdout = check_output([lyx_exe, '-version'], stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError:
         error('Cannot query LyX about the lyx2lyx script.')
     re_msvc = re.compile(r'^(\s*)(Host type:)(\s+)(win32)$')
     re_sysdir = re.compile(r'^(\s*)(LyX files dir:)(\s+)(\S+)$')
-- 
1.7.9.5

Reply via email to