From d7f453cc3461ec44cc66177c2d327df836dbdeda Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Mon, 19 Nov 2012 20:58:42 +0100 Subject: [PATCH] Add Python3 support. --- doc/changelog.txt | 1 + doc/install.txt | 2 +- patool | 7 +++-- patoolib/__init__.py | 48 +++++++++++++++++-------------- patoolib/baker.py | 11 +++++-- patoolib/programs/py_echo.py | 3 +- patoolib/util.py | 29 +++++++++++-------- setup.py | 56 ++++++++++++++++++------------------ tests/__init__.py | 16 +++++++---- 9 files changed, 97 insertions(+), 76 deletions(-) diff --git a/doc/changelog.txt b/doc/changelog.txt index 99b7524..579e0cf 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,6 +1,7 @@ 0.18 "" (released xx.xx.xxxx) * Fixed unadf archive listing. +* Added support for Python 3.x and depend on Python >= 2.7. 0.17 "I am Bruce Lee" (released 4.8.2012) diff --git a/doc/install.txt b/doc/install.txt index cdfeab0..d47ec05 100644 --- a/doc/install.txt +++ b/doc/install.txt @@ -3,7 +3,7 @@ Installation First, install the required software. -1. Python >= 2.5 from http://www.python.org/ +1. Python >= 2.7 from http://www.python.org/ Be sure to also have installed the included distutils module. On most distributions, the distutils module is included in diff --git a/patool b/patool index 6db42d8..ab44c61 100755 --- a/patool +++ b/patool @@ -17,6 +17,7 @@ """ patool [extract|list|create|formats] [sub-command-options] """ +from __future__ import print_function import os import sys from patoolib import handle_archive, list_formats, baker @@ -35,7 +36,7 @@ def handle_multi_archive(archives, cmd, **kwargs): if not os.path.isfile(archive): res = 1 msg = "archive %r is not a file" % archive - print >>sys.stderr, "patool error:", msg + print("patool error:", msg, file=sys.stderr) else: newres = handle_archive(archive, cmd, **kwargs) # return error if one of the archives could not be extracted @@ -94,7 +95,7 @@ def formats (): try: sys.exit(baker.run()) -except baker.CommandError, msg: - print >>sys.stderr, "patool error:", msg +except baker.CommandError as msg: + print("patool error:", msg, file=sys.stderr) baker.help(sys.argv[0]) sys.exit(1) diff --git a/patoolib/__init__.py b/patoolib/__init__.py index 8eb8c7b..1ba29f4 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -13,12 +13,14 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import print_function import sys -if not hasattr(sys, "version_info") or sys.version_info < (2, 5, 0, "final", 0): - raise SystemExit("This program requires Python 2.5 or later.") +if not hasattr(sys, "version_info") or sys.version_info < (2, 7, 0, "final", 0): + raise SystemExit("This program requires Python 2.7 or later.") import os import shutil import stat +import importlib from . import util # Supported archive commands @@ -310,29 +312,29 @@ def program_supports_compression (program, compression): def list_formats (): """Print information about available archive formats to stdout.""" for format in ArchiveFormats: - print format, "files:" + print(format, "files:") for command in ArchiveCommands: programs = ArchivePrograms[format] if command not in programs and None not in programs: - print " %8s: - (not supported)" % command + print(" %8s: - (not supported)" % command) continue try: program = find_archive_program(format, command) - print " %8s: %s" % (command, program), + print(" %8s: %s" % (command, program), end=' ') if format == 'tar': encs = [x for x in ArchiveCompressions if util.find_program(x)] if encs: - print "(supported compressions: %s)" % ", ".join(encs), + print("(supported compressions: %s)" % ", ".join(encs), end=' ') elif format == '7z': if util.p7zip_supports_rar(): - print "(rar archives supported)", + print("(rar archives supported)", end=' ') else: - print "(rar archives not supported)", - print + print("(rar archives not supported)", end=' ') + print() except util.PatoolError: handlers = programs.get(None, programs.get(command)) - print " %8s: - (no program found; install %s)" % \ - (command, util.strlist_with_or(handlers)) + print(" %8s: - (no program found; install %s)" % + (command, util.strlist_with_or(handlers))) return 0 @@ -521,15 +523,17 @@ def _handle_archive (archive, command, *args, **kwargs): def get_archive_cmdlist_func (program, command, format): # get python module for given archive program key = util.stripext(os.path.basename(program).lower()) - module = ProgramModules.get(key, key) - # import archive handler function (eg. patoolib.programs.star.extract_tar) - args = (module, command, format) - import_cmd = "from .programs.%s import %s_%s as func" % args + modulename = ".programs." + ProgramModules.get(key, key) + # import the module try: - exec import_cmd - except ImportError: - raise util.PatoolError('ImportError executing %r' % import_cmd) - return locals()['func'] + module = importlib.import_module(modulename, __name__) + except ImportError as msg: + raise util.PatoolError(msg) + # get archive handler function (eg. patoolib.programs.star.extract_tar) + try: + return getattr(module, '%s_%s' % (command, format)) + except AttributeError as msg: + raise util.PatoolError(msg) def rmtree_log_error (func, path, exc): @@ -542,7 +546,7 @@ def _diff_archives (archive1, archive2, **kwargs): """Show differences between two archives.""" if util.is_same_file(archive1, archive2): msg = "no differences found: archive `%s' and `%s' are the same files" - print msg % (archive1, archive2) + print(msg % (archive1, archive2)) return 0 diff = util.find_program("diff") if not diff: @@ -588,10 +592,10 @@ def handle_archive (archive, command, *args, **kwargs): except KeyboardInterrupt: util.log_error("aborted") res = 1 - except util.PatoolError, msg: + except util.PatoolError as msg: util.log_error(msg) res = 1 - except StandardError, msg: + except Exception as msg: util.log_internal_error() res = 1 return res diff --git a/patoolib/baker.py b/patoolib/baker.py index ec283e2..3cf3db1 100644 --- a/patoolib/baker.py +++ b/patoolib/baker.py @@ -82,6 +82,11 @@ def format_paras(paras, width, indent=0): def totype(v, default): """Tries to convert the value 'v' into the same type as 'default'. """ + # Python3 does not have long, so fake it + try: + long + except NameError: + long = int t = type(default) if t is int: @@ -188,10 +193,10 @@ class Baker(object): if defaults: # Zip up the keyword argument names with their defaults - keywords = dict(zip(arglist[0-len(defaults):], defaults)) + keywords = dict(list(zip(arglist[0-len(defaults):], defaults))) elif has_kwargs: # Allow keyword arguments specified by params. - keywords = dict(zip(params.keys(), [""]*len(params))) + keywords = dict(list(zip(params.keys(), [""]*len(params)))) # But set a flag to detect this self.param_keywords = True else: @@ -409,7 +414,7 @@ class Baker(object): # Process short option(s) # For each character after the '-'... - for i in xrange(1, len(arg)): + for i in range(1, len(arg)): char = arg[i] if char not in shortchars: continue diff --git a/patoolib/programs/py_echo.py b/patoolib/programs/py_echo.py index e5d0ed7..e0fd942 100644 --- a/patoolib/programs/py_echo.py +++ b/patoolib/programs/py_echo.py @@ -15,6 +15,7 @@ # along with this program. If not, see . """Archive commands echoing data, implemented by the Python print statement.""" +from __future__ import print_function from .. import util @@ -60,5 +61,5 @@ def list_flac (archive, compression, cmd, **kwargs): def stripext (cmd, archive, extension=""): """Print the name without suffix.""" - print util.stripext(archive)+extension + print(util.stripext(archive)+extension) return None diff --git a/patoolib/util.py b/patoolib/util.py index 775eefa..66269c6 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Utility functions.""" +from __future__ import print_function import os import sys import subprocess @@ -30,7 +31,7 @@ def init_mimedb(): global mimedb try: mimedb = mimetypes.MimeTypes(strict=False) - except StandardError, msg: + except Exception as msg: log_error("could not initialize MIME database: %s" % msg) return add_mimedb_data(mimedb) @@ -78,7 +79,7 @@ def add_mimetype(mimedb, mimetype, extension): mimedb.add_type(mimetype, extension, strict=strict) -class PatoolError (StandardError): +class PatoolError (Exception): """Raised when errors occur.""" pass @@ -324,7 +325,7 @@ def set_mode (filename, flags): if not (mode & flags): try: os.chmod(filename, flags | mode) - except OSError, msg: + except OSError as msg: log_error("could not set mode flags for `%s': %s" % (filename, msg)) @@ -375,20 +376,20 @@ def get_single_outfile (directory, archive, extension=""): def log_error (msg, out=sys.stderr): """Print error message to stderr (or any other given output).""" - print >> out, "patool error:", msg + print("patool error:", msg, file=out) def log_info (msg, out=sys.stdout): """Print info message to stdout (or any other given output).""" - print >> out, "patool:", msg + print("patool:", msg, file=out) def log_internal_error (out=sys.stderr): """Print internal error message to stderr.""" - print >> out, "patool: internal error" + print("patool: internal error", file=out) traceback.print_exc() - print >> out, "System info:" - print >> out, "Python %s on %s" % (sys.version, sys.platform) + print("System info:", file=out) + print("Python %s on %s" % (sys.version, sys.platform), file=out) def p7zip_supports_rar (): @@ -421,13 +422,17 @@ def append_to_path (path, directory): def get_nt_7z_dir (): """Return 7-Zip directory from registry, or an empty string.""" + # Python 3.x renamed the _winreg module to winreg try: - import _winreg - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\7-Zip") + import _winreg as winreg + except ImportError: + import winreg + try: + key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\7-Zip") try: - return _winreg.QueryValueEx(key, "Path")[0] + return winreg.QueryValueEx(key, "Path")[0] finally: - _winreg.CloseKey(key) + winreg.CloseKey(key) except WindowsError: return "" diff --git a/setup.py b/setup.py index 5541e6f..12a25ff 100644 --- a/setup.py +++ b/setup.py @@ -17,10 +17,10 @@ """ Setup file for the distuils module. """ - +from __future__ import print_function import sys -if not hasattr(sys, "version_info") or sys.version_info < (2, 4, 0, "final", 0): - raise SystemExit("This program requires Python 2.4 or later.") +if not hasattr(sys, "version_info") or sys.version_info < (2, 7, 0, "final", 0): + raise SystemExit("This program requires Python 2.7 or later.") import os import shutil import glob @@ -164,37 +164,37 @@ class InnoScript: def write_inno_script (self, fd): """Write Inno script contents.""" - print >> fd, "; WARNING: This script has been created by py2exe. Changes to this script" - print >> fd, "; will be overwritten the next time py2exe is run!" - print >> fd, "[Setup]" - print >> fd, "AppName=%s" % self.name - print >> fd, "AppVerName=%s %s" % (self.name, self.version) - print >> fd, r"DefaultDirName={pf}\%s" % self.name - print >> fd, "DefaultGroupName=%s" % self.name - print >> fd, "OutputBaseFilename=%s" % self.distfilebase - print >> fd, "OutputDir=.." - print >> fd, "SetupIconFile=%s" % self.icon - print >> fd + print("; WARNING: This script has been created by py2exe. Changes to this script", file=fd) + print("; will be overwritten the next time py2exe is run!", file=fd) + print("[Setup]", file=fd) + print("AppName=%s" % self.name, file=fd) + print("AppVerName=%s %s" % (self.name, self.version), file=fd) + print(r"DefaultDirName={pf}\%s" % self.name, file=fd) + print("DefaultGroupName=%s" % self.name, file=fd) + print("OutputBaseFilename=%s" % self.distfilebase, file=fd) + print("OutputDir=..", file=fd) + print("SetupIconFile=%s" % self.icon, file=fd) + print(file=fd) # List of source files files = self.windows_exe_files + \ self.console_exe_files + \ self.service_exe_files + \ self.comserver_files + \ self.lib_files - print >> fd, '[Files]' + print('[Files]', file=fd) for path in files: - print >> fd, r'Source: "%s"; DestDir: "{app}\%s"; Flags: ignoreversion' % (path, os.path.dirname(path)) + print(r'Source: "%s"; DestDir: "{app}\%s"; Flags: ignoreversion' % (path, os.path.dirname(path)), file=fd) # Set icon filename - print >> fd, '[Icons]' + print('[Icons]', file=fd) for path in self.windows_exe_files: - print >> fd, r'Name: "{group}\%s"; Filename: "{app}\%s"' % \ - (self.name, path) - print >> fd, r'Name: "{group}\Uninstall %s"; Filename: "{uninstallexe}"' % self.name - print >> fd + print(r'Name: "{group}\%s"; Filename: "{app}\%s"' % + (self.name, path), file=fd) + print(r'Name: "{group}\Uninstall %s"; Filename: "{uninstallexe}"' % self.name, file=fd) + print(file=fd) # Uninstall optional log files - print >> fd, '[UninstallDelete]' - print >> fd, r'Type: files; Name: "{pf}\%s\patool*.exe.log"' % self.name - print >> fd + print('[UninstallDelete]', file=fd) + print(r'Type: files; Name: "{pf}\%s\patool*.exe.log"' % self.name, file=fd) + print(file=fd) def compile (self): """Compile Inno script with iscc.exe.""" @@ -209,7 +209,7 @@ class InnoScript: cmd = ['signtool.exe', 'sign', '/f', pfxfile, self.distfile] subprocess.check_call(cmd) else: - print "No signed installer: certificate %s not found." % pfxfile + print("No signed installer: certificate %s not found." % pfxfile) try: from py2exe.build_exe import py2exe as py2exe_build @@ -222,16 +222,16 @@ try: """Generate py2exe installer.""" # First, let py2exe do it's work. py2exe_build.run(self) - print "*** preparing the inno setup script ***" + print("*** preparing the inno setup script ***") lib_dir = self.lib_dir dist_dir = self.dist_dir # create the Installer, using the files py2exe has created. script = InnoScript(lib_dir, dist_dir, self.windows_exe_files, self.console_exe_files, self.service_exe_files, self.comserver_files, self.lib_files) - print "*** creating the inno setup script ***" + print("*** creating the inno setup script ***") script.create() - print "*** compiling the inno setup script ***" + print("*** compiling the inno setup script ***") script.compile() script.sign() except ImportError: diff --git a/tests/__init__.py b/tests/__init__.py index 3364b5f..b34b177 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -14,12 +14,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os +import sys import patoolib import pytest basedir = os.path.dirname(__file__) datadir = os.path.join(basedir, 'data') +# Python 3.x renamed the function name attribute +if sys.version_info[0] > 2: + fnameattr = '__name__' +else: + fnameattr = 'func_name' def needs_os (name): """Decorator skipping test if given program is not available.""" @@ -28,7 +34,7 @@ def needs_os (name): if os.name != name: raise pytest.skip("operating system %s not found" % name) return f(*args, **kwargs) - newfunc.func_name = f.func_name + setattr(newfunc, fnameattr, getattr(f, fnameattr)) return newfunc return check_prog @@ -40,7 +46,7 @@ def needs_program (program): if not patoolib.util.find_program(program): raise pytest.skip("program `%s' not available" % program) return f(*args, **kwargs) - newfunc.func_name = f.func_name + setattr(newfunc, fnameattr, getattr(f, fnameattr)) return newfunc return check_prog @@ -55,7 +61,7 @@ def needs_one_program (programs): else: raise pytest.skip("None of programs %s available" % programs) return f(*args, **kwargs) - newfunc.func_name = f.func_name + setattr(newfunc, fnameattr, getattr(f, fnameattr)) return newfunc return check_prog @@ -69,7 +75,7 @@ def needs_codec (program, codec): if not has_codec(program, codec): raise pytest.skip("codec `%s' for program `%s' not available" % (codec, program)) return f(*args, **kwargs) - newfunc.func_name = f.func_name + setattr(newfunc, fnameattr, getattr(f, fnameattr)) return newfunc return check_prog @@ -81,5 +87,3 @@ def has_codec (program, codec): if patoolib.program_supports_compression(program, codec): return True return patoolib.util.find_program(codec) - -