From cee88ecb6046ebf0ce75e9e8d1a29d4e0abd76fc Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Mon, 25 Feb 2013 21:04:02 +0100 Subject: [PATCH] Replace baker with argparse; some code restructuration. --- doc/changelog.txt | 3 +- doc/patool.1 | 75 ++-- doc/patool.txt | 104 +++--- patool | 182 ++++++---- patoolib/__init__.py | 210 +++++------- patoolib/baker.py | 586 -------------------------------- patoolib/programs/__init__.py | 8 +- patoolib/programs/ar.py | 8 +- patoolib/programs/arc.py | 4 +- patoolib/programs/arj.py | 22 +- patoolib/programs/bzip2.py | 4 +- patoolib/programs/cabextract.py | 6 +- patoolib/programs/compress.py | 4 +- patoolib/programs/cpio.py | 8 +- patoolib/programs/dpkg.py | 4 +- patoolib/programs/flac.py | 8 +- patoolib/programs/gzip.py | 4 +- patoolib/programs/lcab.py | 4 +- patoolib/programs/lha.py | 10 +- patoolib/programs/lrzip.py | 6 +- patoolib/programs/lzop.py | 8 +- patoolib/programs/nomarch.py | 4 +- patoolib/programs/p7zip.py | 28 +- patoolib/programs/py_bz2.py | 45 ++- patoolib/programs/py_gzip.py | 44 +-- patoolib/programs/py_lzma.py | 44 +-- patoolib/programs/py_tarfile.py | 44 +-- patoolib/programs/py_zipfile.py | 53 ++- patoolib/programs/rar.py | 21 +- patoolib/programs/rpm.py | 6 +- patoolib/programs/rpm2cpio.py | 4 +- patoolib/programs/rzip.py | 7 +- patoolib/programs/shar.py | 4 +- patoolib/programs/star.py | 8 +- patoolib/programs/tar.py | 10 +- patoolib/programs/unace.py | 13 +- patoolib/programs/uncompress.py | 4 +- patoolib/programs/unzip.py | 8 +- patoolib/programs/xdms.py | 4 +- patoolib/programs/xz.py | 4 +- patoolib/programs/zip.py | 6 +- patoolib/util.py | 102 +++++- setup.py | 2 +- tests/__init__.py | 1 + tests/archives/__init__.py | 43 +-- tests/archives/test_bsdtar.py | 2 +- tests/archives/test_tar.py | 2 +- tests/archives/test_unadf.py | 2 +- tests/test_baker.py | 65 ---- tests/test_create.py | 35 ++ tests/test_diff.py | 11 +- tests/test_extract.py | 32 ++ tests/test_formats.py | 6 +- tests/test_list.py | 27 ++ tests/test_repack.py | 14 +- tests/test_search.py | 16 +- tests/test_test.py | 27 ++ 57 files changed, 771 insertions(+), 1245 deletions(-) delete mode 100644 patoolib/baker.py delete mode 100644 tests/test_baker.py create mode 100644 tests/test_create.py create mode 100644 tests/test_extract.py create mode 100644 tests/test_list.py create mode 100644 tests/test_test.py diff --git a/doc/changelog.txt b/doc/changelog.txt index 4e89e47..9ab1977 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,6 +1,7 @@ 1.0 "" (released xx.xx.2013) -* Add support for grepping in archive contents. +* Add support for searching in archive contents. +* Allow multiple --verbose options to increase program output. * Fixed Python lzma archive handling. * Fixed lzop, lrzip and rzip archive handling. * Fixed Python 3.x support. diff --git a/doc/patool.1 b/doc/patool.1 index 973cdd6..0542285 100644 --- a/doc/patool.1 +++ b/doc/patool.1 @@ -15,15 +15,9 @@ .SH NAME patool - portable command line archive file manager .SH SYNOPSIS - \fBpatool\fP (\fBlist\fP|\fBtest\fP) [\fB\-\-verbose\fP] <\fIarchive-file\fP>... - \fBpatool\fP \fBextract\fP [\fB\-\-verbose\fP] [\fB\-\-outdir=\fP\fIDIRNAME\fP] <\fIarchive-file\fP>... - \fBpatool\fP \fBcreate\fP [\fB\-\-verbose\fP] <\fIarchive-file\fP> [\fIfiles\fP...] - \fBpatool\fP \fBdiff\fP [\fB\-\-verbose\fP] <\fIarchive1\fP> <\fIarchive2\fP> - \fBpatool\fP \fBsearch\fP [\fB\-\-verbose\fP] <\fIpattern\fP> <\fIarchive-file\fP>... - \fBpatool\fP \fBrepack\fP [\fB\-\-verbose\fP] <\fIarchive1\fP> <\fIarchive2\fP> - \fBpatool\fP \fBformats\fP + \fBpatool\fP [\fIglobal-options\fP] (\fBlist\fP|\fBtest\fP|\fBextract\fP|\fBcreate\fP|\fBdiff\fP|\fBsearch\fP|\fBrepack\fP|\fBformats\fP) [\fIcommand-options\fP] <\fIcommand-arguments\fP>... .SH DESCRIPTION -Various archive types can be created, extracted, tested and listed by +Various archive types can be created, extracted, tested, listed, searched, repacked and compared by \fBpatool\fP. The advantage of patool is its simplicity in handling archive files without having to remember a myriad of programs and options. .PP @@ -39,67 +33,90 @@ TAR (.tar), XZ (.xz), ZIP (.zip, .jar) and ZOO (.zoo) formats. It relies on helper applications to handle those archive formats (for example bzip2 for BZIP2 archives). .PP -The archive formats TAR (.tar), ZIP (.zip), BZIP2 (.bz2) and GZIP (.gz) +The archive formats TAR, ZIP, BZIP2 and GZIP are supported natively and do not require helper applications to be installed. .SH EXAMPLES \fBpatool extract archive.zip otherarchive.rar\fP - \fBpatool test --verbose dist.tar.gz\fP + \fBpatool --verbose test dist.tar.gz\fP \fBpatool list package.deb\fP - \fPpatool create --verbose myfiles.zip file1.txt dir/\fP + \fPpatool --verbose create myfiles.zip file1.txt dir/\fP \fBpatool diff release1.0.tar.gz release2.0.zip\fP \fBpatool search "def urlopen" python-3.3.tar.gz\fP \fBpatool repack linux-2.6.33.tar.gz linux-2.6.33.tar.bz2\fP +.SH GLOBAL OPTIONS +.TP +\fB\-v\fP, \fB\-\-verbose\fP +Display more info about what patool does, and display the output +of helper applications. Can be given multiple times to increase +the output even more. .SH COMMANDS The following rules apply to all commands: .IP "\(bu" 4 Existing files are never overwritten. .IP "\(bu" 4 The original archive will never be removed. -.IP "\(bu" 4 -Verbose operation displays more info about what patool does. It also -displays more info from the helper application if it's supported. .PP -Several commands are available. +The following commands are available. .SS extract -Extract files from an archive. The original archive will never -be removed and is left as it is. -.br -This is the default command if no command was given. +\fBpatool\fP \fBextract\fP [\fIoptions\fP] <\fIarchive\fP>... +.PP +Extract files from given archives. The original archives will never +be removed and are left as is. +.PP +Options: +.TP +\fB\-\-outdir\fP DIRECTORY +Extract to the given output directory. Default is to extract to +the current working directory. .PP If the archive contains exactly one -file or directory, the archive contents are extracted to the current -working directory. -Else the files are extracted in a newly created subdirectory of the current -working directory. The new directory is named after the archive filename without +file or directory, the archive contents are extracted directly to the +output directory. +Else the files are extracted in a newly created subdirectory of the output +directory. The new directory is named after the archive filename without the extension. .br -This prevents cluttering the current working directory with a lot +This prevents cluttering the output directory with a lot of files from the extracted archive. .PP All extracted files are ensured that they are readable by the current user. .SS list -List files in an archive. +\fBpatool\fP \fBlist\fP <\fIarchive\fP>... +.PP +List files in archives. .SS create -Create an archive from given files. At least on of the given files to add -to the archive has to exist (non-existing files are ignored). +\fBpatool\fP \fBcreate\fP <\fIarchive\fP> <\fIfile-or-directory\fP>... +.PP +Create an archive from given files. All of the given files to add +to the archive must be readable by the current user. The format of the archive to create is determined by the archive file extension. .SS test -Test files in an archive. If the helper application does not support +\fBpatool\fP \fBtest\fP <\fIarchive\fP>... +.PP +Test the given archives. If the helper application does not support testing, the archive contents are listed instead. .SS diff +\fBpatool\fP \fBdiff\fP <\fIarchive1\fP> <\fIarchive2\fP> +.PP Show differences between two archives with the \fBdiff(1)\fP program. The diff options used are \fB\-urN\fP. .SS search +\fBpatool\fP \fBsearch\fP <\fIpattern\fP> <\fIarchive\fP> +.PP Search in archive contents for given pattern using the \fBgrep(1)\fP program. The grep options used are \fB\-r\fP; additional options can be supplied with the \fBGREP_OPTIONS\fP environment variable. .SS repack +\fBpatool\fP \fBrepack\fP <\fIarchive\fP> <\fIarchive_new\fP> +.PP Repackage archive to a different format. The target archive format is -determined by the file extension. +determined by the file extension of \fIarchive_new\fP. .SS formats +\fBpatool\fP \fBformats\fP +.PP Show all supported archive formats (ie. which helper applications are available). .SH HELP OPTION diff --git a/doc/patool.txt b/doc/patool.txt index 765ddb0..a237c9c 100644 --- a/doc/patool.txt +++ b/doc/patool.txt @@ -6,46 +6,47 @@ NAME patool - portable command line archive file manager SYNOPSIS - patool (list|test) [--verbose] ... - patool extract [--verbose] [--outdir=DIRNAME] ... - patool create [--verbose] [files...] - patool diff [--verbose] - patool search [--verbose] ... - patool repack [--verbose] - patool formats + patool [global-options] + (list|test|extract|create|diff|search|repack|formats) + [command-options] ... DESCRIPTION - Various archive types can be created, extracted, tested and - listed by patool. The advantage of patool is its simplicity in - handling archive files without having to remember a myriad of - programs and options. + Various archive types can be created, extracted, tested, + listed, searched, repacked and compared by patool. The advan‐ + tage of patool is its simplicity in handling archive files + without having to remember a myriad of programs and options. - The archive format is determined by the file(1) program and as + The archive format is determined by the file(1) program and as a fallback by the archive file extension. patool supports 7z (.7z), ACE (.ace), ADF (.adf), ALZIP (.alz), - APE (.ape), AR (.a), ARC (.arc), ARJ (.arj), BZIP2 (.bz2), CAB - (.cab), compress (.Z), CPIO (.cpio), DEB (.deb), DMS (.dms), - FLAC (.flac), GZIP (.gz), LRZIP (.lrz), LZH (.lha, .lzh), LZIP - (.lz), LZMA (.lzma), LZOP (.lzo), RPM (.rpm), RAR (.rar), RZIP - (.rz), SHN (.shn), TAR (.tar), XZ (.xz), ZIP (.zip, .jar) and + APE (.ape), AR (.a), ARC (.arc), ARJ (.arj), BZIP2 (.bz2), CAB + (.cab), compress (.Z), CPIO (.cpio), DEB (.deb), DMS (.dms), + FLAC (.flac), GZIP (.gz), LRZIP (.lrz), LZH (.lha, .lzh), LZIP + (.lz), LZMA (.lzma), LZOP (.lzo), RPM (.rpm), RAR (.rar), RZIP + (.rz), SHN (.shn), TAR (.tar), XZ (.xz), ZIP (.zip, .jar) and ZOO (.zoo) formats. It relies on helper applications to handle those archive formats (for example bzip2 for BZIP2 archives). - The archive formats TAR (.tar), ZIP (.zip), BZIP2 (.bz2) and - GZIP (.gz) are supported natively and do not require helper - applications to be installed. + The archive formats TAR, ZIP, BZIP2 and GZIP are supported + natively and do not require helper applications to be + installed. EXAMPLES patool extract archive.zip otherarchive.rar - patool test --verbose dist.tar.gz + patool --verbose test dist.tar.gz patool list package.deb - patool create --verbose myfiles.zip file1.txt dir/ + patool --verbose create myfiles.zip file1.txt dir/ patool diff release1.0.tar.gz release2.0.zip patool search "def urlopen" python-3.3.tar.gz patool repack linux-2.6.33.tar.gz linux-2.6.33.tar.bz2 +GLOBAL OPTIONS + -v, --verbose + Display more info about what patool does, and display + the output of helper applications. Can be given multiple + times to increase the output even more. + COMMANDS The following rules apply to all commands: @@ -53,55 +54,72 @@ COMMANDS · The original archive will never be removed. - · Verbose operation displays more info about what patool - does. It also displays more info from the helper applica‐ - tion if it's supported. - - Several commands are available. + The following commands are available. extract - Extract files from an archive. The original archive will never - be removed and is left as it is. - This is the default command if no command was given. + patool extract [options] ... + + Extract files from given archives. The original archives will + never be removed and are left as is. + + Options: + + --outdir DIRECTORY + Extract to the given output directory. Default is to + extract to the current working directory. If the archive contains exactly one file or directory, the ar‐ - chive contents are extracted to the current working directory. + chive contents are extracted directly to the output directory. Else the files are extracted in a newly created subdirectory of - the current working directory. The new directory is named after - the archive filename without the extension. - This prevents cluttering the current working directory with a - lot of files from the extracted archive. + the output directory. The new directory is named after the ar‐ + chive filename without the extension. + This prevents cluttering the output directory with a lot of + files from the extracted archive. All extracted files are ensured that they are readable by the current user. list - List files in an archive. + patool list ... + + List files in archives. create - Create an archive from given files. At least on of the given - files to add to the archive has to exist (non-existing files - are ignored). The format of the archive to create is deter‐ - mined by the archive file extension. + patool create ... + + Create an archive from given files. All of the given files to + add to the archive must be readable by the current user. The + format of the archive to create is determined by the archive + file extension. test - Test files in an archive. If the helper application does not + patool test ... + + Test the given archives. If the helper application does not support testing, the archive contents are listed instead. diff + patool diff + Show differences between two archives with the diff(1) program. The diff options used are -urN. search + patool search + Search in archive contents for given pattern using the grep(1) program. The grep options used are -r; additional options can be supplied with the GREP_OPTIONS environment variable. repack + patool repack + Repackage archive to a different format. The target archive - format is determined by the file extension. + format is determined by the file extension of archive_new. formats + patool formats + Show all supported archive formats (ie. which helper applica‐ tions are available). diff --git a/patool b/patool index 662c3e5..b797892 100755 --- a/patool +++ b/patool @@ -15,93 +15,149 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -patool [extract|list|create|formats] [sub-command-options] +patool [global-options] {extract|list|create|diff|search|formats} [sub-command-options] """ -from __future__ import print_function -import os import sys -from patoolib import handle_archive, list_formats, baker - -# parameter help and short options -params = { - "verbose": "Verbose operation (if the helper application supports it).", -} -shortopts = {"verbose": "v"} +import argparse +import patoolib +from patoolib.util import log_error, log_internal_error, PatoolError -def handle_multi_archive(archives, cmd, **kwargs): - """Handle a multi-archive command.""" +def run_extract(args): + """Extract files from archive(s).""" res = 0 - for archive in archives: - if not os.path.isfile(archive): - res = 1 - msg = "archive %r is not a file" % archive - 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 - if newres: - res = newres + for archive in args.archive: + try: + patoolib.extract_archive(archive, verbosity=args.verbosity, outdir=args.outdir) + except PatoolError as msg: + log_error("error extracting %s: %s" % (archive, msg)) + res += 1 return res -extract_params = { - "outdir": "Extract to given directory.", -} -extract_params.update(params) -@baker.command(default=True, shortopts=shortopts, params=extract_params) -def extract (archive, *archives, **kwargs): - """Extract files from archive(s).""" - return handle_multi_archive((archive,)+archives, 'extract', **kwargs) - - -@baker.command(shortopts=shortopts, params=params) -def list (archive, *archives, **kwargs): +def run_list(args): """List files in archive(s).""" - return handle_multi_archive((archive,)+archives, 'list', **kwargs) + res = 0 + for archive in args.archive: + try: + patoolib.list_archive(archive, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error listing %s: %s" % (archive, msg)) + res += 1 + return res -@baker.command(shortopts=shortopts, params=params) -def test (archive, *archives, **kwargs): +def run_test(args): """Test files in archive(s).""" - return handle_multi_archive((archive,)+archives, 'test', **kwargs) + res = 0 + for archive in args.archive: + try: + patoolib.test_archive(archive, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error testing %s: %s" % (archive, msg)) + res += 1 + return res -@baker.command(shortopts=shortopts, params=params) -def create (archive, file1, *files, **kwargs): +def run_create(args): """Create an archive from given files.""" - return handle_archive(archive, 'create', file1, *files, **kwargs) + res = 0 + try: + patoolib.create_archive(args.archive, args.filename, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error creating %s: %s" % (args.archive, msg)) + res = 1 + return res -@baker.command(shortopts=shortopts, params=params) -def diff (archive1, archive2, **kwargs): +def run_diff(args): """Show differences between two archives.""" - from patoolib import diff - return diff(archive1, archive2, **kwargs) + try: + res = patoolib.diff_archives(args.archive1, args.archive2, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error showing differences between %s and %s: %s" % (args.archive1, args.archive2, msg)) + res = 2 + return res -@baker.command(shortopts=shortopts, params=params) -def search(pattern, archive1, *archives, **kwargs): - """Search for pattern in all given archives.""" - from patoolib import search - return search(pattern, archive1, *archives, **kwargs) +def run_search(args): + """Search for pattern in given archive.""" + try: + res = patoolib.search_archive(args.pattern, args.archive, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error searching %s: %s" % (args.archive, msg)) + res = 2 + return res -@baker.command(shortopts=shortopts, params=params) -def repack (archive1, archive2, **kwargs): +def run_repack(args): """Repackage one archive in another format.""" - from patoolib import repack - return repack(archive1, archive2, **kwargs) + res = 0 + try: + patoolib.repack_archive(args.archive_src, args.archive_dst, verbosity=args.verbosity) + except PatoolError as msg: + log_error("error repacking %s: %s" % (args.archive_src, msg)) + res = 1 + return res -@baker.command -def formats (): +def run_formats (args): """List supported and available archive formats.""" - return list_formats() + patoolib.list_formats() + return 0 -try: - sys.exit(baker.run()) -except baker.CommandError as msg: - print("patool error:", msg, file=sys.stderr) - baker.help(sys.argv[0]) - sys.exit(1) + +def create_argparser(): + """Construct and return an argument parser.""" + parser = argparse.ArgumentParser(description="A commandline archive handler.") + parser.add_argument('--verbose', '-v', action='count', default=0, dest='verbosity', help="verbose operation; can be given multiple times") + subparsers = parser.add_subparsers(help='the archive command; type "patool COMMAND -h" for command-specific help', dest='command') + # extract + parser_extract = subparsers.add_parser('extract', help='extract one or more archives') + parser_extract.add_argument('--outdir', help="output directory to extract to") + parser_extract.add_argument('archive', nargs='+', help="an archive file") + # list + parser_list = subparsers.add_parser('list', help='list members or one or more archives') + parser_list.add_argument('archive', nargs='+', help="an archive file") + # create + parser_create = subparsers.add_parser('create', help='create an archive') + parser_create.add_argument('archive', help="the archive file; the file extension determines the archive program") + parser_create.add_argument('filename', nargs='+', help="a file or directory to add to the archive; note that some archive programs do not support directories") + # test + parser_test = subparsers.add_parser('test', help='test an archive') + parser_test.add_argument('archive', nargs='+', help='an archive file') + # repack + parser_repack = subparsers.add_parser('repack', help='repack an archive to a different format') + parser_repack.add_argument('archive_src', help='source archive file') + parser_repack.add_argument('archive_dst', help='target archive file') + # diff + parser_diff = subparsers.add_parser('diff', help='show differences between two archives') + parser_diff.add_argument('archive1', help='the first archive file') + parser_diff.add_argument('archive2', help='the second archive file') + # search + parser_search = subparsers.add_parser('search', help="search contents of archive members") + parser_search.add_argument('pattern', help='the grep(1) compatible search pattern') + parser_search.add_argument('archive', help='the archive file') + # formats + subparsers.add_parser('formats', help="show supported archive formats") + return parser + + +def main(): + """Parse options and execute commands.""" + try: + argparser = create_argparser() + args = argparser.parse_args() + # run subcommand function + return globals()["run_%s" % args.command](args) + except KeyboardInterrupt: + log_error("aborted") + res = 1 + except Exception: + log_internal_error() + res = 2 + return res + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/patoolib/__init__.py b/patoolib/__init__.py index 0b859cf..f60fdd6 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -22,6 +22,9 @@ import shutil import stat import importlib from . import util +__all__ = ['list_formats', 'list_archive', 'extract_archive', 'test_archive', + 'create_archive', 'diff_archives', 'search_archive', 'repack_archive'] + # Supported archive commands ArchiveCommands = ('list', 'extract', 'test', 'create') @@ -348,10 +351,9 @@ def list_formats (): handlers = programs.get(None, programs.get(command)) print(" %8s: - (no program found; install %s)" % (command, util.strlist_with_or(handlers))) - return 0 -AllowedConfigKeys = ("verbose", "program") +AllowedConfigKeys = ("verbosity", "program") def clean_config_keys (kwargs): """Remove invalid configuration keys from arguments.""" @@ -369,7 +371,7 @@ def parse_config (archive, format, compression, command, **kwargs): is not supported. """ config = { - 'verbose': False, + 'verbosity': 0, } config['program'] = find_archive_program(format, command) for key, value in kwargs.items(): @@ -410,14 +412,14 @@ def move_outdir_orphan (outdir): return (False, "multiple files in root") -def run_archive_cmdlist (archive_cmdlist): +def run_archive_cmdlist (archive_cmdlist, verbosity=0): """Run archive command.""" # archive_cmdlist is a command list with optional keyword arguments if isinstance(archive_cmdlist, tuple): cmdlist, runkwargs = archive_cmdlist else: cmdlist, runkwargs = archive_cmdlist, {} - util.run_checked(cmdlist, **runkwargs) + return util.run_checked(cmdlist, verbosity=verbosity, **runkwargs) def make_file_readable (filename): @@ -458,31 +460,6 @@ def cleanup_outdir (outdir, archive): return outdir2, "`%s' (%s)" % (outdir2, msg) -def check_archive_arguments (archive, command, *args): - """Check for invalid archive command arguments.""" - if command == 'create': - util.check_archive_filelist(args) - util.check_new_filename(archive) - elif command == 'repack': - util.check_existing_filename(archive) - if not args: - raise util.PatoolError("missing target archive filename for repack") - util.check_new_filename(args[0]) - elif command == 'diff': - util.check_existing_filename(archive) - if not args: - raise util.PatoolError("missing second archive filename for diff") - util.check_existing_filename(args[0]) - elif command == 'search': - if not archive: - # archive is the search pattern - raise util.PatoolError("empty search pattern") - for arg in args: - util.check_existing_filename(arg) - else: - util.check_existing_filename(archive) - - def _handle_archive (archive, command, *args, **kwargs): """Handle archive command; raising PatoolError on errors. @return: output directory if command is 'extract', else None @@ -494,16 +471,13 @@ def _handle_archive (archive, command, *args, **kwargs): check_archive_command(command) config_kwargs = clean_config_keys(kwargs) config = parse_config(archive, format, compression, command, **config_kwargs) - # check if archive already exists - if command == 'create' and os.path.exists(archive): - raise util.PatoolError("archive `%s' already exists" % archive) program = config['program'] get_archive_cmdlist = get_archive_cmdlist_func(program, command, format) # prepare keyword arguments for command list - cmd_kwargs = dict(verbose=config['verbose']) + cmd_kwargs = dict(verbosity=config['verbosity']) origarchive = None if command == 'extract': - if "outdir" in kwargs: + if kwargs.get('outdir'): cmd_kwargs["outdir"] = kwargs["outdir"] do_cleanup_outdir = False else: @@ -520,13 +494,14 @@ def _handle_archive (archive, command, *args, **kwargs): # an empty command list means the get_archive_cmdlist() function # already handled the command (eg. when it's a builtin Python # function) - run_archive_cmdlist(cmdlist) + run_archive_cmdlist(cmdlist, verbosity=config['verbosity']) if command == 'extract': if do_cleanup_outdir: target, msg = cleanup_outdir(cmd_kwargs["outdir"], archive) - util.log_info("%s extracted to %s" % (archive, msg)) else: target, msg = cmd_kwargs["outdir"], "`%s'" % cmd_kwargs["outdir"] + if config['verbosity'] >= 0: + util.log_info("... %s extracted to %s." % (archive, msg)) return target elif command == 'create' and origarchive: shutil.move(archive, origarchive) @@ -561,11 +536,12 @@ def rmtree_log_error (func, path, exc): util.log_error(msg) -def _diff_archives (archive1, archive2, **kwargs): - """Show differences between two archives.""" +def _diff_archives (archive1, archive2, verbosity=0): + """Show differences between two archives. + @return 0 if archives are the same, else 1 + @raises: PatoolError on errors + """ if util.is_same_file(archive1, archive2): - msg = "no differences found: archive `%s' and `%s' are the same files" - util.log_info(msg % (archive1, archive2)) return 0 diff = util.find_program("diff") if not diff: @@ -573,65 +549,51 @@ def _diff_archives (archive1, archive2, **kwargs): raise util.PatoolError(msg) tmpdir1 = util.tmpdir() try: - path1 = _handle_archive(archive1, 'extract', outdir=tmpdir1, **kwargs) + path1 = _handle_archive(archive1, 'extract', outdir=tmpdir1, verbosity=-1) tmpdir2 = util.tmpdir() try: - path2 = _handle_archive(archive2, 'extract', outdir=tmpdir2, **kwargs) - return util.run([diff, "-urN", path1, path2]) + path2 = _handle_archive(archive2, 'extract', outdir=tmpdir2, verbosity=-1) + return util.run_checked([diff, "-urN", path1, path2], verbosity=1, ret_ok=(0, 1)) finally: shutil.rmtree(tmpdir2, onerror=rmtree_log_error) finally: shutil.rmtree(tmpdir1, onerror=rmtree_log_error) -def _search_archives(pattern, *archives, **kwargs): - """Search for given pattern in all archives.""" +def _search_archive(pattern, archive, verbosity=0): + """Search for given pattern in an archive.""" grep = util.find_program("grep") if not grep: msg = "The grep(1) program is required for searching archive contents, please install it." raise util.PatoolError(msg) - errors = 0 - for archive in archives: - try: - errors += _search_archive(grep, pattern, archive, **kwargs) - except util.PatoolError as msg: - util.log_error("grep error: %s" % msg) - errors += 1 - return errors - - -def _search_archive(grep, pattern, archive, **kwargs): - """Extract one archive and search for given pattern in extracted files.""" tmpdir = util.tmpdir() try: - _handle_archive(archive, 'extract', outdir=tmpdir, **kwargs) - return util.run([grep, "-r", "-e", pattern, "."], cwd=tmpdir) + path = _handle_archive(archive, 'extract', outdir=tmpdir, verbosity=-1) + return util.run_checked([grep, "-r", "-e", pattern, "."], ret_ok=(0, 1), verbosity=1, cwd=path) finally: shutil.rmtree(tmpdir, onerror=rmtree_log_error) -def _repack_archive (archive1, archive2, **kwargs): +def _repack_archive (archive1, archive2, verbosity=0): """Repackage an archive to a different format.""" format1, compression1 = get_archive_format(archive1) format2, compression2 = get_archive_format(archive2) if format1 == format2 and compression1 == compression2: # same format and compression allows to copy the file - try: - util.link_or_copy(archive1, archive2, verbose=kwargs.get('verbose')) - return 0 - except OSError: - return 1 + util.link_or_copy(archive1, archive2, verbosity=verbosity) + return tmpdir = util.tmpdir() try: + kwargs = dict(verbosity=verbosity, outdir=tmpdir) same_format = (format1 == format2 and compression1 and compression2) if same_format: # only decompress since the format is the same kwargs['format'] = compression1 - _handle_archive(archive1, 'extract', outdir=tmpdir, **kwargs) + path = _handle_archive(archive1, 'extract', **kwargs) archive = os.path.abspath(archive2) - files = tuple(os.listdir(tmpdir)) + files = tuple(os.listdir(path)) olddir = os.getcwd() - os.chdir(tmpdir) + os.chdir(path) try: if same_format: # only compress since the format is the same @@ -639,68 +601,82 @@ def _repack_archive (archive1, archive2, **kwargs): _handle_archive(archive, 'create', *files, **kwargs) finally: os.chdir(olddir) - return 0 finally: shutil.rmtree(tmpdir, onerror=rmtree_log_error) -def handle_archive (archive, command, *args, **kwargs): - """Handle archive file command; with nice error reporting.""" - check_archive_arguments(archive, command, *args) - try: - if command == "diff": - res = _diff_archives(archive, args[0], **kwargs) - elif command == "search": - res = _search_archives(archive, *args, **kwargs) - elif command == "repack": - res = _repack_archive(archive, args[0], **kwargs) - else: - _handle_archive(archive, command, *args, **kwargs) - res = 0 - except KeyboardInterrupt: - util.log_error("aborted") - res = 1 - except util.PatoolError as msg: - util.log_error(msg) - res = 1 - except Exception as msg: - util.log_internal_error() - res = 1 +# the patool library API + +def extract_archive(archive, verbosity=0, outdir=None, program=None): + """Extract given archive.""" + util.check_existing_filename(archive) + if verbosity >= 0: + util.log_info("Extracting %s ..." % archive) + return _handle_archive(archive, 'extract', verbosity=verbosity, outdir=outdir, program=program) + + +def list_archive(archive, verbosity=0, program=None): + """List given archive.""" + util.check_existing_filename(archive) + if verbosity >= 0: + util.log_info("Listing %s ..." % archive) + return _handle_archive(archive, 'list', verbosity=verbosity, program=program) + + +def test_archive(archive, verbosity=0, program=None): + """Test given archive.""" + util.check_existing_filename(archive) + if verbosity >= 0: + util.log_info("Testing %s ..." % archive) + res = _handle_archive(archive, 'test', verbosity=verbosity, program=program) + if verbosity >= 0: + util.log_info("... tested ok.") return res -# convenience functions - -def extract (archive, verbose=False, outdir=None): - """Extract given archive.""" - return handle_archive(archive, 'extract', verbose=verbose, outdir=outdir) - - -def list (archive, verbose=False): - """List given archive.""" - return handle_archive(archive, 'list', verbose=verbose) - - -def test (archive, verbose=False): - """Test given archive.""" - return handle_archive(archive, 'test', verbose=verbose) - - -def create (archive, *filenames, **kwargs): +def create_archive(archive, filenames, verbosity=0, program=None): """Create given archive with given files.""" - return handle_archive(archive, 'create', *filenames, **kwargs) + util.check_new_filename(archive) + util.check_archive_filelist(filenames) + if verbosity >= 0: + util.log_info("Creating %s ..." % archive) + res = _handle_archive(archive, 'create', *tuple(filenames), verbosity=verbosity, program=program) + if verbosity >= 0: + util.log_info("... %s created." % archive) + return res -def diff (archive1, archive2, verbose=False): +def diff_archives(archive1, archive2, verbosity=0): """Print differences between two archives.""" - return handle_archive(archive1, 'diff', archive2, verbose=verbose) + util.check_existing_filename(archive1) + util.check_existing_filename(archive2) + if verbosity >= 0: + util.log_info("Comparing %s with %s ..." % (archive1, archive2)) + res = _diff_archives(archive1, archive2, verbosity=verbosity) + if res == 0 and verbosity >= 0: + util.log_info("... no differences found.") -def search(pattern, *archives, **kwargs): +def search_archive(pattern, archive, verbosity=0): """Search pattern in archive members.""" - return handle_archive(pattern, 'search', *archives, **kwargs) + if not pattern: + raise util.PatoolError("empty search pattern") + util.check_existing_filename(archive) + if verbosity >= 0: + util.log_info("Searching %r in %s ..." % (pattern, archive)) + res = _search_archive(pattern, archive, verbosity=verbosity) + if res == 1 and verbosity >= 0: + util.log_info("... %r not found" % pattern) + return res -def repack (archive1, archive2, verbose=False): +def repack_archive (archive, archive_new, verbosity=0): """Repack archive to different file and/or format.""" - return handle_archive(archive1, 'repack', archive2, verbose=verbose) + util.check_existing_filename(archive) + util.check_new_filename(archive_new) + if verbosity >= 0: + util.log_info("Repacking %s to %s ..." % (archive, archive_new)) + res = _repack_archive(archive, archive_new, verbosity=verbosity) + if verbosity >= 0: + util.log_info("... repacking successful.") + return res diff --git a/patoolib/baker.py b/patoolib/baker.py deleted file mode 100644 index c2cf8b3..0000000 --- a/patoolib/baker.py +++ /dev/null @@ -1,586 +0,0 @@ -#=============================================================================== -# Copyright 2010 Matt Chaput -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#=============================================================================== - -import re, sys -from inspect import getargspec -from textwrap import wrap - - -def normalize_docstring(docstring): - """Normalizes whitespace in the given string. - """ - return re.sub(r"[\r\n\t ]+", " ", docstring).strip() - - -param_exp = re.compile(r"^([\t ]*):param (.*?): ([^\n]*\n(\1[ \t]+[^\n]*\n)*)", - re.MULTILINE) - -def find_param_docs(docstring): - """Finds ReStructuredText-style ":param:" lines in the docstring and - returns a dictionary mapping param names to doc strings. - """ - - paramdocs = {} - for match in param_exp.finditer(docstring): - name = match.group(2) - value = match.group(3) - paramdocs[name] = value - return paramdocs - -def remove_param_docs(docstring): - """Finds ReStructuredText-style ":param:" lines in the docstring and - returns a new string with the param documentation removed. - """ - return param_exp.sub("", docstring) - - -def process_docstring(docstring): - """Takes a docstring and returns a list of strings representing - the paragraphs in the docstring. - """ - - lines = docstring.split("\n") - paras = [[]] - for line in lines: - if not line.strip(): - paras.append([]) - else: - paras[-1].append(line) - paras = [normalize_docstring(" ".join(ls)) - for ls in paras if ls] - return paras - - -def format_paras(paras, width, indent=0): - """Takes a list of paragraph strings and formats them into a word-wrapped, - optionally indented string. - """ - - output = [] - for para in paras: - lines = wrap(para, width-indent) - if lines: - for line in lines: - output.append((" " * indent) + line) - output.append("") - return "\n".join(output) - - -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: - return int(v) - elif t is float: - return float(v) - elif t is long: - return long(v) - elif t is bool: - lv = v.lower() - if lv in ("true", "yes", "on", "1"): - return True - elif lv in ("false", "no", "off", "0"): - return False - else: - raise TypeError - else: - return v - - -class CommandError(Exception): - """Error for baker commands.""" - pass - - -class Cmd(object): - """Stores metadata about a command. - """ - - def __init__(self, name, fn, argnames, keywords, shortopts, - has_varargs, has_kwargs, docstring, paramdocs): - """Store given metadata.""" - self.name = name - self.fn = fn - self.argnames = argnames - self.keywords = keywords - self.shortopts = shortopts - self.has_varargs = has_varargs - self.has_kwargs = has_kwargs - self.docstring = docstring - self.paramdocs = paramdocs - self.param_keywords = False - - -class Baker(object): - """Main class.""" - - def __init__(self): - """Initialize command variables.""" - self.commands = {} - self.defaultcommand = None - - def command(self, fn=None, name=None, default=False, - params=None, shortopts=None): - """Registers a command with the bakery. This does not call the - function, it simply adds it to the list of functions this Baker - knows about. - - This method is usually used as a decorator:: - - b = Baker() - - @b.command - def test(): - pass - - :param fn: the function to register. - :param name: use this argument to register the command under a - different name than the function name. - :param default: if True, this command is used when a command is not - specified on the command line. - :param params: a dictionary mapping parameter names to docstrings. If - you don't specify this argument, parameter annotations will be used - (Python 3.x only), or the functions docstring will be searched for - Sphinx-style ':param' blocks. - :param shortopts: a dictionary mapping parameter names to short - options, e.g. {"verbose": "v"}. - """ - - # This method works as a decorator with or without arguments. - if fn is None: - # The decorator was given arguments, e.g. @command(default=True), - # so we have to return a function that will wrap the function when - # the decorator is applied. - return lambda fn: self.command(fn, default=default, - params=params, - shortopts=shortopts) - name = name or fn.__name__ - - # Inspect the argument signature of the function - arglist, vargsname, kwargsname, defaults = getargspec(fn) - has_varargs = bool(vargsname) - has_kwargs = bool(kwargsname) - - # Get the function's docstring - docstring = fn.__doc__ or "" - - # If the user didn't specify parameter help in the decorator - # arguments, try to get it from parameter annotations (Python 3.x) - # or RST-style :param: lines in the docstring - if params is None: - if hasattr(fn, "func_annotations") and fn.func_annotations: - params = fn.func_annotations - else: - params = find_param_docs(docstring) - docstring = remove_param_docs(docstring) - - # If the user didn't specify - shortopts = shortopts or {} - - if defaults: - # Zip up the keyword argument names with their defaults - keywords = dict(list(zip(arglist[0-len(defaults):], defaults))) - elif has_kwargs: - # Allow keyword arguments specified by params. - keywords = dict(list(zip(params.keys(), [""]*len(params)))) - # But set a flag to detect this - self.param_keywords = True - else: - keywords = {} - - # If this is a method, remove 'self' from the argument list - if arglist and arglist[0] == "self": - arglist.pop(0) - - # Create a Cmd object to represent this command and store it - cmd = Cmd(name, fn, arglist, keywords, shortopts, - has_varargs, has_kwargs, - docstring, params) - self.commands[cmd.name] = cmd - - # If default is True, set this as the default command - if default: - self.defaultcommand = cmd - - return fn - - def print_top_help(self, scriptname, file=sys.stdout): - """Prints the documentation for the script and exits. - - :param scriptname: the name of the script being executed (argv[0]). - :param file: the file to write the help to. The default is stdout. - """ - - # Get a sorted list of all command names - cmdnames = sorted(self.commands.keys()) - - # Calculate the indent for the doc strings by taking the longest - # command name and adding 3 (one space before the name and two after) - rindent = max(len(name) for name in cmdnames) + 3 - - # Print the basic help for running a command - file.write("\nUsage: %s COMMAND \n\n" % scriptname) - - print("Available commands:\n\n") - for cmdname in cmdnames: - # Get the Cmd object for this command - cmd = self.commands[cmdname] - - # Calculate the padding necessary to fill from the end of the - # command name to the documentation margin - tab = " " * (rindent - (len(cmdname)+1)) - file.write(" " + cmdname + tab) - - # Get the paragraphs of the command's docstring - paras = process_docstring(cmd.docstring) - if paras: - # Print the first paragraph - file.write(format_paras([paras[0]], 76, indent=rindent).lstrip()) - - file.write("\n") - file.write('Use "%s --help" for individual command help.\n' % scriptname) - sys.exit(0) - - def print_command_help(self, scriptname, cmd, file=sys.stdout): - """Prints the documentation for a specific command and exits. - - :param scriptname: the name of the script being executed (argv[0]). - :param cmd: the Cmd object representing the command. - :param file: the file to write the help to. The default is stdout. - """ - - # Print the usage for the command - file.write("\nUsage: %s %s" % (scriptname, cmd.name)) - - # Print the required and "optional" arguments (where optional - # arguments are keyword arguments with default None). - for name in cmd.argnames: - if name not in cmd.keywords: - # This is a positional argument - file.write(" <%s>" % name) - else: - # This is a keyword argument, so skip it unless the default is - # None, in which case treat it like an optional argument. - if cmd.keywords[name] is None: - file.write(" [<%s>]" % name) - - if cmd.has_varargs: - # This command accepts a variable number of positional arguments - file.write(" [...]") - file.write("\n\n") - - # Print the documentation for this command - paras = process_docstring(cmd.docstring) - if paras: - # Print the first paragraph with no indent (usually just a summary - # line) - file.write(format_paras([paras[0]], 76)) - - # Print subsequent paragraphs indented by 4 spaces - if len(paras) > 1: - file.write("\n") - file.write(format_paras(paras[1:], 76, indent=4)) - file.write("\n") - - # Get a sorted list of keyword argument names - keynames = sorted([key for key, value in cmd.keywords.items() if value is not None]) - # Print documentation for keyword options - if keynames: - file.write("Options:\n\n") - - # Make formatted headings, e.g. " -k --keyword ", and put them in - # a list like [(name, heading), ...] - heads = [] - for keyname in keynames: - if cmd.keywords[keyname] is None: continue - - head = " --" + keyname - if keyname in cmd.shortopts: - head = " -" + cmd.shortopts[keyname] + head - head += " " - heads.append((keyname, head)) - - if heads: - # Find the length of the longest formatted heading - rindent = max(len(head) for keyname, head in heads) - # Pad the headings so they're all as long as the longest one - heads = [(keyname, head + (" " * (rindent - len(head)))) - for keyname, head in heads] - - # Print the option docs - for keyname, head in heads: - # Print the heading - file.write(head) - - # If this parameter has documentation, print it after the - # heading - if keyname in cmd.paramdocs: - paras = process_docstring(cmd.paramdocs.get(keyname, "")) - file.write(format_paras(paras, 76, indent=rindent).lstrip()) - else: - file.write("\n") - file.write("\n") - - if any((cmd.keywords.get(a) is None) for a in cmd.argnames): - file.write("(specifying a single hyphen (-) in the argument list means all\n") - file.write("subsequent arguments are treated as bare arguments, not options)\n") - file.write("\n") - - sys.exit(0) - - def parse_args(self, scriptname, cmd, argv): - """Parse command arguments.""" - keywords = cmd.keywords - shortopts = cmd.shortopts - - # shortopts maps long option names to characters. To look up short - # options, we need to create a reverse mapping. - shortchars = dict((v, k) for k, v in shortopts.items()) - - # The *vargs list and **kwargs dict to build up from the command line - # arguments - vargs = [] - kwargs = {} - - while argv: - # Take the next argument - arg = argv.pop(0) - - if arg == "--": - # All arguments following a double hyphen are treated as - # positional arguments - vargs.extend(argv) - break - - elif arg == "-": - # What to do with a bare -? Right now, it's ignored. - continue - - elif arg.startswith("--"): - # Process long option - - value = None - if "=" in arg: - # The argument was specified like --keyword=value - name, value = arg[2:].split("=", 1) - default = keywords.get(name) - try: - value = totype(value, default) - except TypeError: - pass - else: - # The argument was not specified with a value, assuming - # this is a flag - name = arg[2:] - default = keywords.get(name) - - if type(default) is bool: - # If this option is a boolean, it doesn't need a value; - # specifying it on the command line means "do the - # opposite of the default". - value = not default - else: - # The next item in the argument list is the value, i.e. - # --keyword value - if not argv or argv[0].startswith("-") or self.param_keywords: - # There isn't a value available, or its derived - # from parameters. Just use True, assuming this - # is a flag. - value = True - else: - value = argv.pop(0) - try: - value = totype(value, default) - except TypeError: - pass - - # Store this option - kwargs[name] = value - - elif arg.startswith("-") and cmd.shortopts: - # Process short option(s) - - # For each character after the '-'... - for i in range(1, len(arg)): - char = arg[i] - if char not in shortchars: - continue - - # Get the long option name corresponding to this char - name = shortchars[char] - - default = keywords[name] - if type(default) is bool: - # If this option is a boolean, it doesn't need a value; - # specifying it on the command line means "do the - # opposite of the default". - kwargs[name] = not default - else: - # This option requires a value... - if self.param_keywords: - # It's derived from parameters. Just use True, - # assuming this is a flag. - value = True - elif i < len(arg)-1: - # There are other characters after this one, so - # the rest of the characters must represent the - # value (i.e. old-style UNIX option like -Nname) - value = totype(arg[i+1:], default) - elif argv: - # This is the last character in the list, so the - # next argument on the command line is the value. - value = argv.pop(0) - else: - # There is no value available. Just use True, - # assuming this is a flag. - value = True - - try: - kwargs[name] = totype(value, default) - except ValueError: - raise CommandError("Couldn't convert %s value %r to type %s" % (name, value, type(default))) - break - else: - # This doesn't start with "-", so just add it to the list of - # positional arguments. - vargs.append(arg) - - return vargs, kwargs - - def parse(self, argv=None): - """Parses the command and parameters to call from the list of command - line arguments. Returns a tuple of (Cmd object, position arg list, - keyword arg dict). - - :param argv: the list of options passed to the command line (sys.argv). - """ - - if argv is None: argv = sys.argv - - scriptname = argv[0] - - if (len(argv) < 2) or (argv[1] == "-h" or argv[1] == "--help"): - # Print the documentation for the script - self.print_top_help(scriptname) - - if argv[1] == "help": - if len(argv) > 2 and argv[2] in self.commands: - cmd = self.commands[argv[2]] - self.print_command_help(scriptname, cmd) - self.print_top_help(scriptname) - - if len(argv) > 1 and argv[1] in self.commands: - # The first argument on the command line (after the script name - # is the command to run. - cmd = self.commands[argv[1]] - - if len(argv) > 2 and (argv[2] == "-h" or argv[2] == "--help"): - # Print the help for this command and exit - self.print_command_help(scriptname, cmd) - - options = argv[2:] - else: - # No known command was specified. If there's a default command, - # use that. - cmd = self.defaultcommand - if cmd is None: - raise CommandError("unknown command `%s' specified" % argv[1]) - - options = argv[1:] - - # Parse the rest of the arguments on the command line and use them to - # call the command function. - args, kwargs = self.parse_args(scriptname, cmd, options) - return (cmd, args, kwargs) - - def apply(self, cmd, args, kwargs): - """Calls the command function. - """ - - # Create a list of positional arguments: arguments that are either - # required (not in keywords), or where the default is None (taken to be - # an optional positional argument). This is different from the Python - # calling convention, which will fill in keyword arguments with extra - # positional arguments. - posargs = [a for a in cmd.argnames if cmd.keywords.get(a) is None] - - if len(args) > len(posargs) and not cmd.has_varargs: - raise CommandError("Too many arguments to %s: %s" % (cmd.name, " ".join(args))) - - if cmd.keywords: - for k in sorted(kwargs.keys()): - if k not in cmd.keywords: - raise CommandError("Unknown option --%s" % k) - - # Rearrange the arguments into the order Python expects - newargs = [] - newkwargs = dict(kwargs) - for name in cmd.argnames: - if args and cmd.keywords.get(name) is None: - # This argument is required or optional and we have a bare arg - # to fill it - newargs.append(args.pop(0)) - elif name not in cmd.keywords and not args: - # This argument is required but we don't have a bare arg to - # fill it - raise CommandError("Required argument '%s' not given" % name) - else: - # This is a keyword argument - newkwargs[name] = kwargs.get(name, cmd.keywords[name]) - newargs.extend(args) - - return cmd.fn(*newargs, **newkwargs) - - def run(self, argv=None): - """Takes a list of command line arguments, parses it into a command - name and options, and calls the function corresponding to the command - with the given arguments. - - :param argv: the list of options passed to the command line (sys.argv). - """ - - return self.apply(*self.parse(argv)) - - def test(self, argv=None): - """Takes a list of command line arguments, parses it into a command - name and options, and returns what the resulting function call would - look like. This may be useful for testing how command line arguments - would be passed to your functions. - - :param argv: the list of options passed to the command line (sys.argv). - """ - - cmd, args, kwargs = self.parse(argv) - result = "%s(%s" % (cmd.name, ",".join(repr(a) for a in args)) - if kwargs: - kws = ",".join("%s=%r" % (k, v) for k, v in kwargs.items()) - result += "," + kws - result += ")" - return result - - -_baker = Baker() -command = _baker.command -run = _baker.run -test = _baker.test -help = _baker.print_top_help diff --git a/patoolib/programs/__init__.py b/patoolib/programs/__init__.py index 96f004f..d20cbde 100644 --- a/patoolib/programs/__init__.py +++ b/patoolib/programs/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ from .. import util def extract_singlefile_standard (archive, compression, cmd, **kwargs): """Standard routine to extract a singlefile archive (like gzip).""" cmdlist = [util.shell_quote(cmd)] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') outfile = util.get_single_outfile(kwargs['outdir'], archive) cmdlist.extend(['-c', '-d', '--', util.shell_quote(archive), '>', @@ -29,7 +29,7 @@ def extract_singlefile_standard (archive, compression, cmd, **kwargs): def test_singlefile_standard (archive, compression, cmd, **kwargs): """Standard routine to test a singlefile archive (like gzip).""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-t', '--', archive]) return cmdlist @@ -38,7 +38,7 @@ def test_singlefile_standard (archive, compression, cmd, **kwargs): def create_singlefile_standard (archive, compression, cmd, *args, **kwargs): """Standard routine to create a singlefile archive (like gzip).""" cmdlist = [util.shell_quote(cmd)] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-c', '--']) cmdlist.extend([util.shell_quote(x) for x in args]) diff --git a/patoolib/programs/ar.py b/patoolib/programs/ar.py index d72a979..e3800e9 100644 --- a/patoolib/programs/ar.py +++ b/patoolib/programs/ar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ import os def extract_ar (archive, compression, cmd, **kwargs): """Extract a AR archive.""" opts = 'x' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' cmdlist = [cmd, opts, os.path.abspath(archive)] return (cmdlist, {'cwd': kwargs['outdir']}) @@ -27,7 +27,7 @@ def extract_ar (archive, compression, cmd, **kwargs): def list_ar (archive, compression, cmd, **kwargs): """List a AR archive.""" opts = 't' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' return [cmd, opts, archive] @@ -36,7 +36,7 @@ test_ar = list_ar def create_ar (archive, compression, cmd, *args, **kwargs): """Create a AR archive.""" opts = 'rc' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' cmdlist = [cmd, opts, archive] cmdlist.extend(args) diff --git a/patoolib/programs/arc.py b/patoolib/programs/arc.py index 6edc620..8a8ade5 100644 --- a/patoolib/programs/arc.py +++ b/patoolib/programs/arc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ def extract_arc (archive, compression, cmd, **kwargs): def list_arc (archive, compression, cmd, **kwargs): """List a ARC archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('v') else: cmdlist.append('l') diff --git a/patoolib/programs/arj.py b/patoolib/programs/arj.py index 9b94cd4..f94c773 100644 --- a/patoolib/programs/arj.py +++ b/patoolib/programs/arj.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,39 +17,27 @@ def extract_arj (archive, compression, cmd, **kwargs): """Extract a ARJ archive.""" - cmdlist = [cmd, 'x', '-r', '-y'] - if not kwargs['verbose']: - cmdlist.append('-i-') - cmdlist.extend([archive, kwargs['outdir']]) - return cmdlist + return [cmd, 'x', '-r', '-y', archive, kwargs['outdir']] def list_arj (archive, compression, cmd, **kwargs): """List a ARJ archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('v') else: cmdlist.append('l') - cmdlist.append('-i-') cmdlist.extend(['-r', '-y', archive]) return cmdlist def test_arj (archive, compression, cmd, **kwargs): """Test a ARJ archive.""" - cmdlist = [cmd, 't'] - if not kwargs['verbose']: - cmdlist.append('-i-') - cmdlist.extend(['-r', '-y', archive]) - return cmdlist + return [cmd, 't', '-r', '-y', archive] def create_arj (archive, compression, cmd, *args, **kwargs): """Create a ARJ archive.""" - cmdlist = [cmd, 'a', '-r', '-y'] - if not kwargs['verbose']: - cmdlist.append('-i-') - cmdlist.append(archive) + cmdlist = [cmd, 'a', '-r', '-y', archive] cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/bzip2.py b/patoolib/programs/bzip2.py index 7ae8a32..d746e7e 100644 --- a/patoolib/programs/bzip2.py +++ b/patoolib/programs/bzip2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ test_bzip2 = test_singlefile_standard def create_bzip2 (archive, compression, cmd, *args, **kwargs): """Create a BZIP2 archive.""" cmdlist = [util.shell_quote(cmd)] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-c', '-z', '--']) cmdlist.extend([util.shell_quote(x) for x in args]) diff --git a/patoolib/programs/cabextract.py b/patoolib/programs/cabextract.py index 334f661..80f45c0 100644 --- a/patoolib/programs/cabextract.py +++ b/patoolib/programs/cabextract.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def extract_cab (archive, compression, cmd, **kwargs): """Extract a CAB archive.""" cmdlist = [cmd, '-d', kwargs['outdir']] - if kwargs['verbose']: + if kwargs['verbosity'] > 0: cmdlist.append('-v') cmdlist.append(archive) return cmdlist @@ -26,7 +26,7 @@ def extract_cab (archive, compression, cmd, **kwargs): def list_cab (archive, compression, cmd, **kwargs): """List a CAB archive.""" cmdlist = [cmd, '-l'] - if kwargs['verbose']: + if kwargs['verbosity'] > 0: cmdlist.append('-v') cmdlist.append(archive) return cmdlist diff --git a/patoolib/programs/compress.py b/patoolib/programs/compress.py index f03abb8..7cc8632 100644 --- a/patoolib/programs/compress.py +++ b/patoolib/programs/compress.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ from .. import util def create_compress (archive, compression, cmd, *args, **kwargs): """Create a compressed archive.""" cmdlist = [util.shell_quote(cmd)] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append('-c') cmdlist.extend([util.shell_quote(x) for x in args]) diff --git a/patoolib/programs/cpio.py b/patoolib/programs/cpio.py index 84ece33..e48ea30 100644 --- a/patoolib/programs/cpio.py +++ b/patoolib/programs/cpio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ def extract_cpio (archive, compression, cmd, **kwargs): if sys.platform.startswith('linux') and not cmd.endswith('bsdcpio'): cmdlist.extend(['--no-absolute-filenames', '--force-local', '--nonmatching', r'"*\.\.*"']) - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['<', util.shell_quote(os.path.abspath(archive))]) return (cmdlist, {'cwd': kwargs['outdir'], 'shell': True}) @@ -34,7 +34,7 @@ def extract_cpio (archive, compression, cmd, **kwargs): def list_cpio (archive, compression, cmd, **kwargs): """List a CPIO archive.""" cmdlist = [cmd, '-i', '-t'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-F', archive]) return cmdlist @@ -44,7 +44,7 @@ test_cpio = list_cpio def create_cpio(archive, compression, cmd, *args, **kwargs): """Create a CPIO archive.""" cmdlist = [util.shell_quote(cmd), '--create'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') if len(args) != 0: findcmd = ['find'] diff --git a/patoolib/programs/dpkg.py b/patoolib/programs/dpkg.py index 51a12b9..c50538e 100644 --- a/patoolib/programs/dpkg.py +++ b/patoolib/programs/dpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def extract_deb (archive, compression, cmd, **kwargs): """Extract a DEB archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('--vextract') else: cmdlist.append('--extract') diff --git a/patoolib/programs/flac.py b/patoolib/programs/flac.py index a7646f5..a100a55 100644 --- a/patoolib/programs/flac.py +++ b/patoolib/programs/flac.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012 Bastian Kleineidam +# Copyright (C) 2012-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,8 +32,4 @@ def create_flac (archive, compression, cmd, *args, **kwargs): def test_flac (archive, compression, cmd, **kwargs): """Test a FLAC file.""" - cmdlist = [cmd, '--test'] - if not kwargs['verbose']: - cmdlist.append('-s') - cmdlist.append(archive) - return cmdlist + return [cmd, '--test', archive] diff --git a/patoolib/programs/gzip.py b/patoolib/programs/gzip.py index cdf3306..c11ff48 100644 --- a/patoolib/programs/gzip.py +++ b/patoolib/programs/gzip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ create_gzip = create_singlefile_standard def list_gzip (archive, compression, cmd, **kwargs): """List a GZIP archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 0: cmdlist.append('-v') cmdlist.extend(['-l', '--', archive]) return cmdlist diff --git a/patoolib/programs/lcab.py b/patoolib/programs/lcab.py index a4f95c9..c2fb498 100644 --- a/patoolib/programs/lcab.py +++ b/patoolib/programs/lcab.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012 Bastian Kleineidam +# Copyright (C) 2012-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def create_cab (archive, compression, cmd, *args, **kwargs): """Create a CAB archive.""" cmdlist = [cmd, '-r'] - if not kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-q') cmdlist.extend(args) cmdlist.append(archive) diff --git a/patoolib/programs/lha.py b/patoolib/programs/lha.py index f158752..8b8cef8 100644 --- a/patoolib/programs/lha.py +++ b/patoolib/programs/lha.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def extract_lzh (archive, compression, cmd, **kwargs): """Extract a LZH archive.""" opts = 'x' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' opts += "w=%s" % kwargs['outdir'] return [cmd, opts, archive] @@ -26,7 +26,7 @@ def extract_lzh (archive, compression, cmd, **kwargs): def list_lzh (archive, compression, cmd, **kwargs): """List a LZH archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('v') else: cmdlist.append('l') @@ -36,14 +36,14 @@ def list_lzh (archive, compression, cmd, **kwargs): def test_lzh (archive, compression, cmd, **kwargs): """Test a LZH archive.""" opts = 't' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' return [cmd, opts, archive] def create_lzh (archive, compression, cmd, *args, **kwargs): """Create a LZH archive.""" opts = 'a' - if kwargs['verbose']: + if kwargs['verbosity'] > 1: opts += 'v' cmdlist = [cmd, opts, archive] cmdlist.extend(args) diff --git a/patoolib/programs/lrzip.py b/patoolib/programs/lrzip.py index 4706f8e..1e922ab 100644 --- a/patoolib/programs/lrzip.py +++ b/patoolib/programs/lrzip.py @@ -20,7 +20,7 @@ from .. import util def extract_lrzip (archive, compression, cmd, **kwargs): """Extract a LRZIP archive.""" cmdlist = [cmd, '-d'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') outfile = util.get_single_outfile(kwargs['outdir'], archive) cmdlist.extend(["-o", outfile, os.path.abspath(archive)]) @@ -29,7 +29,7 @@ def extract_lrzip (archive, compression, cmd, **kwargs): def test_lrzip (archive, compression, cmd, **kwargs): """Test a LRZIP archive.""" cmdlist = [cmd, '-t'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append(archive) return cmdlist @@ -37,7 +37,7 @@ def test_lrzip (archive, compression, cmd, **kwargs): def create_lrzip (archive, compression, cmd, *args, **kwargs): """Create a LRZIP archive.""" cmdlist = [cmd, '-o', archive] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/lzop.py b/patoolib/programs/lzop.py index 8018541..7f8f990 100644 --- a/patoolib/programs/lzop.py +++ b/patoolib/programs/lzop.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ extract_lzop = extract_singlefile_standard def list_lzop (archive, compression, cmd, **kwargs): """List a LZOP archive.""" cmdlist = [cmd, '--list'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('--verbose') cmdlist.extend(['--', archive]) return cmdlist @@ -31,7 +31,7 @@ def list_lzop (archive, compression, cmd, **kwargs): def test_lzop (archive, compression, cmd, **kwargs): """Test a LZOP archive.""" cmdlist = [cmd, '--test'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('--verbose') cmdlist.extend(['--', archive]) return cmdlist @@ -39,7 +39,7 @@ def test_lzop (archive, compression, cmd, **kwargs): def create_lzop (archive, compression, cmd, *args, **kwargs): """Create a LZOP archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-o', archive, '--']) cmdlist.extend(args) diff --git a/patoolib/programs/nomarch.py b/patoolib/programs/nomarch.py index bbdf198..680a527 100644 --- a/patoolib/programs/nomarch.py +++ b/patoolib/programs/nomarch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ def extract_arc (archive, compression, cmd, **kwargs): def list_arc (archive, compression, cmd, **kwargs): """List a ARC archive.""" cmdlist = [cmd, '-l'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append(archive) return cmdlist diff --git a/patoolib/programs/p7zip.py b/patoolib/programs/p7zip.py index 5abecd9..241bf00 100644 --- a/patoolib/programs/p7zip.py +++ b/patoolib/programs/p7zip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,11 +17,7 @@ def extract_7z (archive, compression, cmd, **kwargs): """Extract a 7z archive.""" - cmdlist = [cmd, 'x'] - if not kwargs['verbose']: - cmdlist.append('-bd') - cmdlist.extend(['-o%s' % kwargs['outdir'], '--', archive]) - return cmdlist + return [cmd, 'x', '-o%s' % kwargs['outdir'], '--', archive] extract_bzip2 = \ extract_gzip = \ @@ -37,12 +33,7 @@ extract_bzip2 = \ def list_7z (archive, compression, cmd, **kwargs): """List a 7z archive.""" - cmdlist = [cmd, 'l'] - if not kwargs['verbose']: - cmdlist.append('-bd') - cmdlist.append('--') - cmdlist.append(archive) - return cmdlist + return [cmd, 'l', '--', archive] list_bzip2 = \ list_gzip = \ @@ -59,12 +50,7 @@ list_bzip2 = \ def test_7z (archive, compression, cmd, **kwargs): """Test a 7z archive.""" - cmdlist = [cmd, 't'] - if not kwargs['verbose']: - cmdlist.append('-bd') - cmdlist.append('--') - cmdlist.append(archive) - return cmdlist + return [cmd, 't', '--', archive] test_bzip2 = \ test_gzip = \ @@ -81,11 +67,7 @@ test_bzip2 = \ def create_7z (archive, compression, cmd, *args, **kwargs): """Create a 7z archive.""" - cmdlist = [cmd, 'a'] - if not kwargs['verbose']: - cmdlist.append('-bd') - cmdlist.append('--') - cmdlist.append(archive) + cmdlist = [cmd, 'a', '--', archive] cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/py_bz2.py b/patoolib/programs/py_bz2.py index 71be192..5f78a26 100644 --- a/patoolib/programs/py_bz2.py +++ b/patoolib/programs/py_bz2.py @@ -21,46 +21,39 @@ try: except ImportError: import bz2 +# read in 1MB chunks READ_SIZE_BYTES = 1024*1024 def extract_bzip2 (archive, compression, cmd, **kwargs): """Extract a BZIP2 archive with the bz2 Python module.""" - verbose = kwargs['verbose'] outdir = kwargs['outdir'] - if verbose: - util.log_info('extracting %s...' % archive) targetname = util.get_single_outfile(outdir, archive) - bz2file = bz2.BZ2File(archive) try: - with open(targetname, 'wb') as targetfile: - data = bz2file.read(READ_SIZE_BYTES) - while data: - targetfile.write(data) + with bz2.BZ2File(archive) as bz2file: + with open(targetname, 'wb') as targetfile: data = bz2file.read(READ_SIZE_BYTES) - finally: - bz2file.close() - if verbose: - util.log_info('... extracted to %s' % targetname) + while data: + targetfile.write(data) + data = bz2file.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error extracting %s to %s: %s" % (archive, targetname, err) + raise util.PatoolError(msg) return None def create_bzip2 (archive, compression, cmd, *args, **kwargs): """Create a BZIP2 archive with the bz2 Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('creating %s...' % archive) if len(args) > 1: - util.log_error('multi-file compression not supported in Python bz2') - bz2file = bz2.BZ2File(archive, 'wb') + raise util.PatoolError('multi-file compression not supported in Python bz2') try: - filename = args[0] - with open(filename, 'rb') as srcfile: - data = srcfile.read(READ_SIZE_BYTES) - while data: - bz2file.write(data) + with bz2.BZ2File(archive, 'wb') as bz2file: + filename = args[0] + with open(filename, 'rb') as srcfile: data = srcfile.read(READ_SIZE_BYTES) - if verbose: - util.log_info('... added %s' % filename) - finally: - bz2file.close() + while data: + bz2file.write(data) + data = srcfile.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error creating %s: %s" % (archive, err) + raise util.PatoolError(msg) return None diff --git a/patoolib/programs/py_gzip.py b/patoolib/programs/py_gzip.py index 7e7a4c4..dd2906b 100644 --- a/patoolib/programs/py_gzip.py +++ b/patoolib/programs/py_gzip.py @@ -23,42 +23,34 @@ READ_SIZE_BYTES = 1024*1024 def extract_gzip (archive, compression, cmd, **kwargs): """Extract a GZIP archive with the gzip Python module.""" - verbose = kwargs['verbose'] outdir = kwargs['outdir'] - if verbose: - util.log_info('extracting %s...' % archive) targetname = util.get_single_outfile(outdir, archive) - gzipfile = gzip.GzipFile(archive) try: - with open(targetname, 'wb') as targetfile: - data = gzipfile.read(READ_SIZE_BYTES) - while data: - targetfile.write(data) + with gzip.GzipFile(archive) as gzipfile: + with open(targetname, 'wb') as targetfile: data = gzipfile.read(READ_SIZE_BYTES) - finally: - gzipfile.close() - if verbose: - util.log_info('... extracted to %s' % targetname) + while data: + targetfile.write(data) + data = gzipfile.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error extracting %s to %s: %s" % (archive, targetname, err) + raise util.PatoolError(msg) return None def create_gzip (archive, compression, cmd, *args, **kwargs): """Create a GZIP archive with the gzip Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('creating %s...' % archive) if len(args) > 1: - util.log_error('multi-file compression not supported in Python gzip') - gzipfile = gzip.GzipFile(archive, 'wb') + raise util.PatoolError('multi-file compression not supported in Python gzip') try: - filename = args[0] - with open(filename, 'rb') as srcfile: - data = srcfile.read(READ_SIZE_BYTES) - while data: - gzipfile.write(data) + with gzip.GzipFile(archive, 'wb') as gzipfile: + filename = args[0] + with open(filename, 'rb') as srcfile: data = srcfile.read(READ_SIZE_BYTES) - if verbose: - util.log_info('... added %s' % filename) - finally: - gzipfile.close() + while data: + gzipfile.write(data) + data = srcfile.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error creating %s: %s" % (archive, err) + raise util.PatoolError(msg) return None diff --git a/patoolib/programs/py_lzma.py b/patoolib/programs/py_lzma.py index c80e1c3..4898ea8 100644 --- a/patoolib/programs/py_lzma.py +++ b/patoolib/programs/py_lzma.py @@ -21,22 +21,18 @@ READ_SIZE_BYTES = 1024*1024 def _extract(archive, compression, cmd, format, **kwargs): """Extract an LZMA or XZ archive with the lzma Python module.""" - verbose = kwargs['verbose'] outdir = kwargs['outdir'] - if verbose: - util.log_info('extracting %s...' % archive) targetname = util.get_single_outfile(outdir, archive) - lzmafile = lzma.LZMAFile(archive, format=format) try: - with open(targetname, 'wb') as targetfile: - data = lzmafile.read(READ_SIZE_BYTES) - while data: - targetfile.write(data) + with lzma.LZMAFile(archive, format=format) as lzmafile: + with open(targetname, 'wb') as targetfile: data = lzmafile.read(READ_SIZE_BYTES) - finally: - lzmafile.close() - if verbose: - util.log_info('... extracted to %s' % targetname) + while data: + targetfile.write(data) + data = lzmafile.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error extracting %s to %s: %s" % (archive, targetname, err) + raise util.PatoolError(msg) return None def extract_lzma(archive, compression, cmd, **kwargs): @@ -50,23 +46,19 @@ def extract_xz(archive, compression, cmd, **kwargs): def _create(archive, compression, cmd, format, *args, **kwargs): """Create an LZMA or XZ archive with the lzma Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('creating %s...' % archive) if len(args) > 1: - util.log_error('multi-file compression not supported in Python lzma') - lzmafile = lzma.LZMAFile(archive, 'wb', format) + raise util.PatoolError('multi-file compression not supported in Python lzma') try: - filename = args[0] - with open(filename, 'rb') as srcfile: - data = srcfile.read(READ_SIZE_BYTES) - while data: - lzmafile.write(data) + with lzma.LZMAFile(archive, 'wb', format) as lzmafile: + filename = args[0] + with open(filename, 'rb') as srcfile: data = srcfile.read(READ_SIZE_BYTES) - if verbose: - util.log_info('... added %s' % filename) - finally: - lzmafile.close() + while data: + lzmafile.write(data) + data = srcfile.read(READ_SIZE_BYTES) + except Exception as err: + msg = "error creating %s: %s" % (archive, err) + raise util.PatoolError(msg) return None def create_lzma(archive, compression, cmd, *args, **kwargs): diff --git a/patoolib/programs/py_tarfile.py b/patoolib/programs/py_tarfile.py index e2b6e9b..53bf2bc 100644 --- a/patoolib/programs/py_tarfile.py +++ b/patoolib/programs/py_tarfile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012 Bastian Kleineidam +# Copyright (C) 2012-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,46 +22,38 @@ READ_SIZE_BYTES = 1024*1024 def list_tar (archive, compression, cmd, **kwargs): """List a TAR archive with the tarfile Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('listing %s...' % archive) - tfile = tarfile.open(archive) try: - tfile.list(verbose=verbose) - finally: - tfile.close() + with tarfile.open(archive) as tfile: + tfile.list(verbose=kwargs["verbosity"]>1) + except Exception as err: + msg = "error listing %s: %s" % (archive, err) + raise util.PatoolError(msg) return None test_tar = list_tar def extract_tar (archive, compression, cmd, **kwargs): """Extract a TAR archive with the tarfile Python module.""" - verbose = kwargs['verbose'] outdir = kwargs['outdir'] - if verbose: - util.log_info('extracting %s...' % archive) - tfile = tarfile.open(archive) try: - tfile.extractall(path=outdir) - finally: - tfile.close() - if verbose: - util.log_info('... extracted to %s' % outdir) + with tarfile.open(archive) as tfile: + tfile.extractall(path=outdir) + except Exception as err: + msg = "error extracting %s: %s" % (archive, err) + raise util.PatoolError(msg) return None def create_tar (archive, compression, cmd, *args, **kwargs): """Create a TAR archive with the tarfile Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('creating %s...' % archive) mode = get_tar_mode(compression) - tfile = tarfile.open(archive, mode) try: - for filename in args: - tfile.add(filename) - finally: - tfile.close() + with tarfile.open(archive, mode) as tfile: + for filename in args: + tfile.add(filename) + except Exception as err: + msg = "error creating %s: %s" % (archive, err) + raise util.PatoolError(msg) return None @@ -73,6 +65,6 @@ def get_tar_mode (compression): return 'w:bz2' if compression: msg = 'pytarfile does not support %s for tar compression' - util.log_error(msg % compression) + raise util.PatoolError(msg % compression) # no compression return 'w' diff --git a/patoolib/programs/py_zipfile.py b/patoolib/programs/py_zipfile.py index 0b4f8d8..3c80170 100644 --- a/patoolib/programs/py_zipfile.py +++ b/patoolib/programs/py_zipfile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012 Bastian Kleineidam +# Copyright (C) 2012-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Archive commands for the zipfile Python module.""" +from __future__ import print_function from .. import util import zipfile import os @@ -23,49 +24,43 @@ READ_SIZE_BYTES = 1024*1024 def list_zip (archive, compression, cmd, **kwargs): """List member of a ZIP archive with the zipfile Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('listing %s...' % archive) - zfile = zipfile.ZipFile(archive, "r") + verbosity = kwargs['verbosity'] try: - for name in zfile.namelist(): - util.log_info('member %s' % name) - finally: - zfile.close() + with zipfile.ZipFile(archive, "r") as zfile: + for name in zfile.namelist(): + if verbosity >= 0: + print(name) + except Exception as err: + msg = "error listing %s: %s" % (archive, err) + raise util.PatoolError(msg) return None test_zip = list_zip def extract_zip (archive, compression, cmd, **kwargs): """Extract a ZIP archive with the zipfile Python module.""" - verbose = kwargs['verbose'] outdir = kwargs['outdir'] - if verbose: - util.log_info('extracting %s...' % archive) - zfile = zipfile.ZipFile(archive) try: - zfile.extractall(outdir) - finally: - zfile.close() - if verbose: - util.log_info('... extracted to %s' % outdir) + with zipfile.ZipFile(archive) as zfile: + zfile.extractall(outdir) + except Exception as err: + msg = "error extracting %s: %s" % (archive, err) + raise util.PatoolError(msg) return None def create_zip (archive, compression, cmd, *args, **kwargs): """Create a ZIP archive with the zipfile Python module.""" - verbose = kwargs['verbose'] - if verbose: - util.log_info('creating %s...' % archive) - zfile = zipfile.ZipFile(archive, 'w') try: - for filename in args: - if os.path.isdir(filename): - write_directory(zfile, filename) - else: - zfile.write(filename) - finally: - zfile.close() + with zipfile.ZipFile(archive, 'w') as zfile: + for filename in args: + if os.path.isdir(filename): + write_directory(zfile, filename) + else: + zfile.write(filename) + except Exception as err: + msg = "error creating %s: %s" % (archive, err) + raise util.PatoolError(msg) return None diff --git a/patoolib/programs/rar.py b/patoolib/programs/rar.py index e240a40..a2db689 100644 --- a/patoolib/programs/rar.py +++ b/patoolib/programs/rar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,36 +18,25 @@ import os def extract_rar (archive, compression, cmd, **kwargs): """Extract a RAR archive.""" - cmdlist = [cmd, 'x'] - if not kwargs['verbose']: - cmdlist.append('-c-') - cmdlist.extend(['--', os.path.abspath(archive)]) + cmdlist = [cmd, 'x', '--', os.path.abspath(archive)] return (cmdlist, {'cwd': kwargs['outdir']}) def list_rar (archive, compression, cmd, **kwargs): """List a RAR archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('v') else: cmdlist.append('l') - cmdlist.append('-c-') cmdlist.extend(['--', archive]) return cmdlist def test_rar (archive, compression, cmd, **kwargs): """Test a RAR archive.""" - cmdlist = [cmd, 't'] - if not kwargs['verbose']: - cmdlist.append('-c-') - cmdlist.extend(['--', archive]) - return cmdlist + return [cmd, 't', '--', archive] def create_rar (archive, compression, cmd, *args, **kwargs): """Create a RAR archive.""" - cmdlist = [cmd, 'a'] - if not kwargs['verbose']: - cmdlist.append('-c-') - cmdlist.extend(['-r', '--', archive]) + cmdlist = [cmd, 'a', '-r', '--', archive] cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/rpm.py b/patoolib/programs/rpm.py index bd22dfc..9210aa8 100644 --- a/patoolib/programs/rpm.py +++ b/patoolib/programs/rpm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def list_rpm (archive, compression, cmd, **kwargs): """List a RPM archive.""" cmdlist = [cmd, '-q', '-l'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-p', '--', archive]) return cmdlist @@ -26,7 +26,7 @@ def list_rpm (archive, compression, cmd, **kwargs): def test_rpm (archive, compression, cmd, **kwargs): """Test a RPM archive.""" cmdlist = [cmd, 'V'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['-p', '--', archive]) return cmdlist diff --git a/patoolib/programs/rpm2cpio.py b/patoolib/programs/rpm2cpio.py index 61ddf6b..e9ba997 100644 --- a/patoolib/programs/rpm2cpio.py +++ b/patoolib/programs/rpm2cpio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,6 +28,6 @@ def extract_rpm (archive, compression, cmd, **kwargs): '--extract', '--make-directories', '--preserve-modification-time', '--no-absolute-filenames', '--force-local', '--nonmatching', r'"*\.\.*"'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') return (cmdlist, {'cwd': kwargs['outdir'], 'shell': True}) diff --git a/patoolib/programs/rzip.py b/patoolib/programs/rzip.py index cfb8c0a..da46d47 100644 --- a/patoolib/programs/rzip.py +++ b/patoolib/programs/rzip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,13 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Archive commands for the rzip program.""" -import os from .. import util def extract_rzip (archive, compression, cmd, **kwargs): """Extract an RZIP archive.""" cmdlist = [cmd, '-d', '-k'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') outfile = util.get_single_outfile(kwargs['outdir'], archive) cmdlist.extend(["-o", outfile, archive]) @@ -29,7 +28,7 @@ def extract_rzip (archive, compression, cmd, **kwargs): def create_rzip (archive, compression, cmd, *args, **kwargs): """Create an RZIP archive.""" cmdlist = [cmd, '-k', '-o', archive] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/shar.py b/patoolib/programs/shar.py index f8033c4..e39e5e8 100644 --- a/patoolib/programs/shar.py +++ b/patoolib/programs/shar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2012 Bastian Kleineidam +# Copyright (C) 2012-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,8 +19,6 @@ from .. import util def create_shar (archive, compression, cmd, *args, **kwargs): """Create a SHAR archive.""" cmdlist = [util.shell_quote(cmd)] - if not kwargs['verbose']: - cmdlist.append('-q') cmdlist.extend([util.shell_quote(x) for x in args]) cmdlist.extend(['>', util.shell_quote(archive)]) return (cmdlist, {'shell': True}) diff --git a/patoolib/programs/star.py b/patoolib/programs/star.py index e51755d..ade947f 100644 --- a/patoolib/programs/star.py +++ b/patoolib/programs/star.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,14 +19,14 @@ from .tar import add_tar_opts as add_star_opts def extract_tar (archive, compression, cmd, **kwargs): """Extract a TAR archive.""" cmdlist = [cmd, '-x'] - add_star_opts(cmdlist, compression, kwargs['verbose']) + add_star_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.extend(['-C', kwargs['outdir'], 'file=%s' % archive]) return cmdlist def list_tar (archive, compression, cmd, **kwargs): """List a TAR archive.""" cmdlist = [cmd, '-n'] - add_star_opts(cmdlist, compression, kwargs['verbose']) + add_star_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.append("file=%s" % archive) return cmdlist @@ -35,7 +35,7 @@ test_tar = list_tar def create_tar (archive, compression, cmd, *args, **kwargs): """Create a TAR archive.""" cmdlist = [cmd, '-c'] - add_star_opts(cmdlist, compression, kwargs['verbose']) + add_star_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.append("file=%s" % archive) cmdlist.extend(args) return cmdlist diff --git a/patoolib/programs/tar.py b/patoolib/programs/tar.py index 96ddd76..85f2fde 100644 --- a/patoolib/programs/tar.py +++ b/patoolib/programs/tar.py @@ -20,14 +20,14 @@ import os def extract_tar (archive, compression, cmd, **kwargs): """Extract a TAR archive.""" cmdlist = [cmd, '--extract'] - add_tar_opts(cmdlist, compression, kwargs['verbose']) + add_tar_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.extend(["--file", archive, '--directory', kwargs['outdir']]) return cmdlist def list_tar (archive, compression, cmd, **kwargs): """List a TAR archive.""" cmdlist = [cmd, '--list'] - add_tar_opts(cmdlist, compression, kwargs['verbose']) + add_tar_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.extend(["--file", archive]) return cmdlist @@ -36,12 +36,12 @@ test_tar = list_tar def create_tar (archive, compression, cmd, *args, **kwargs): """Create a TAR archive.""" cmdlist = [cmd, '--create'] - add_tar_opts(cmdlist, compression, kwargs['verbose']) + add_tar_opts(cmdlist, compression, kwargs['verbosity']) cmdlist.extend(["--file", archive, '--']) cmdlist.extend(args) return cmdlist -def add_tar_opts (cmdlist, compression, verbose): +def add_tar_opts (cmdlist, compression, verbosity): """Add tar options to cmdlist.""" progname = os.path.basename(cmdlist[0]) if compression == 'gzip': @@ -58,5 +58,5 @@ def add_tar_opts (cmdlist, compression, verbose): program = compression # set compression program cmdlist.extend(['--use-compress-program', program]) - if verbose: + if verbosity > 1: cmdlist.append('--verbose') diff --git a/patoolib/programs/unace.py b/patoolib/programs/unace.py index 45a7e7b..5255baf 100644 --- a/patoolib/programs/unace.py +++ b/patoolib/programs/unace.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,8 +18,6 @@ def extract_ace (archive, compression, cmd, **kwargs): """Extract a ACE archive.""" cmdlist = [cmd, 'x'] - if not kwargs['verbose']: - cmdlist.append('-c-') outdir = kwargs['outdir'] if not outdir.endswith('/'): outdir += '/' @@ -29,18 +27,13 @@ def extract_ace (archive, compression, cmd, **kwargs): def list_ace (archive, compression, cmd, **kwargs): """List a ACE archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('v') else: cmdlist.append('l') - cmdlist.append('-c-') cmdlist.append(archive) return cmdlist def test_ace (archive, compression, cmd, **kwargs): """Test a ACE archive.""" - cmdlist = [cmd, 't'] - if not kwargs['verbose']: - cmdlist.append('-c-') - cmdlist.append(archive) - return cmdlist + return [cmd, 't', archive] diff --git a/patoolib/programs/uncompress.py b/patoolib/programs/uncompress.py index 0981c60..bc54d35 100644 --- a/patoolib/programs/uncompress.py +++ b/patoolib/programs/uncompress.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ from .. import util def extract_compress (archive, compression, cmd, **kwargs): """Extract a compressed archive.""" cmdlist = [util.shell_quote(cmd)] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') outfile = util.get_single_outfile(kwargs['outdir'], archive) cmdlist.extend(['-c', util.shell_quote(archive), '>', diff --git a/patoolib/programs/unzip.py b/patoolib/programs/unzip.py index 0d8b440..2d09aba 100644 --- a/patoolib/programs/unzip.py +++ b/patoolib/programs/unzip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def extract_zip (archive, compression, cmd, **kwargs): """Extract a ZIP archive.""" cmdlist = [cmd] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['--', archive, '-d', kwargs['outdir']]) return cmdlist @@ -26,7 +26,7 @@ def extract_zip (archive, compression, cmd, **kwargs): def list_zip (archive, compression, cmd, **kwargs): """List a ZIP archive.""" cmdlist = [cmd, '-l'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['--', archive]) return cmdlist @@ -34,7 +34,7 @@ def list_zip (archive, compression, cmd, **kwargs): def test_zip (archive, compression, cmd, **kwargs): """Test a ZIP archive.""" cmdlist = [cmd, '-t'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['--', archive]) return cmdlist diff --git a/patoolib/programs/xdms.py b/patoolib/programs/xdms.py index dfe659d..dd2e59f 100644 --- a/patoolib/programs/xdms.py +++ b/patoolib/programs/xdms.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2011-2012 Bastian Kleineidam +# Copyright (C) 2011-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ def extract_dms (archive, compression, cmd, **kwargs): """Extract a DMS archive.""" check_archive_ext(archive) cmdlist = [cmd, '-d', kwargs['outdir']] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.extend(['u', archive]) return cmdlist diff --git a/patoolib/programs/xz.py b/patoolib/programs/xz.py index 5023de7..df72f27 100644 --- a/patoolib/programs/xz.py +++ b/patoolib/programs/xz.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ def list_xz (archive, compression, cmd, **kwargs): """List a XZ archive.""" cmdlist = [cmd] cmdlist.append('-l') - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append(archive) return cmdlist diff --git a/patoolib/programs/zip.py b/patoolib/programs/zip.py index 0f386a9..c6dca9b 100644 --- a/patoolib/programs/zip.py +++ b/patoolib/programs/zip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ def create_zip (archive, compression, cmd, *args, **kwargs): """Create a ZIP archive.""" cmdlist = [cmd, '-r'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append(archive) cmdlist.extend(args) @@ -27,7 +27,7 @@ def create_zip (archive, compression, cmd, *args, **kwargs): def test_zip (archive, compression, cmd, *args, **kwargs): """Test a ZIP archive.""" cmdlist = [cmd, '--test'] - if kwargs['verbose']: + if kwargs['verbosity'] > 1: cmdlist.append('-v') cmdlist.append(archive) return cmdlist diff --git a/patoolib/util.py b/patoolib/util.py index faae4ca..15ffb57 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -21,9 +21,14 @@ import shutil import subprocess import mimetypes import tempfile +import time import traceback from distutils.spawn import find_executable +AppName = "patool" +App = "%s 1.0" % AppName +SupportUrl = "https://github.com/wummel/patool/issues" + # internal MIME database mimedb = None @@ -120,25 +125,34 @@ def backtick (cmd, encoding='utf-8'): return data.decode(encoding) -def run (cmd, **kwargs): +def run (cmd, verbosity=0, **kwargs): """Run command without error checking. @return: command return code""" # Note that shell_quote_nt() result is not suitable for copy-paste # (especially on Unix systems), but it looks nicer than shell_quote(). - log_info("running %s" % " ".join(map(shell_quote_nt, cmd))) + if verbosity >= 0: + log_info("running %s" % " ".join(map(shell_quote_nt, cmd))) if kwargs: - log_info(" with %s" % ", ".join("%s=%s" % (k, shell_quote(str(v)))\ + if verbosity >= 0: + log_info(" with %s" % ", ".join("%s=%s" % (k, shell_quote(str(v)))\ for k, v in kwargs.items())) if kwargs.get("shell"): # for shell calls the command must be a string cmd = " ".join(cmd) - return subprocess.call(cmd, **kwargs) + if verbosity < 1: + # hide command output on stdout + with open(os.devnull, 'wb') as devnull: + kwargs['stdout'] = devnull + res = subprocess.call(cmd, **kwargs) + else: + res = subprocess.call(cmd, **kwargs) + return res -def run_checked (cmd, **kwargs): +def run_checked (cmd, ret_ok=(0,), **kwargs): """Run command and raise PatoolError on error.""" retcode = run(cmd, **kwargs) - if retcode: + if retcode not in ret_ok: msg = "Command `%s' returned non-zero exit status %d" % (cmd, retcode) raise PatoolError(msg) return retcode @@ -391,12 +405,69 @@ def log_info (msg, out=sys.stdout): print("patool:", msg, file=out) -def log_internal_error (out=sys.stderr): - """Print internal error message to stderr.""" - print("patool: internal error", file=out) - traceback.print_exc() +def log_internal_error(out=sys.stderr, etype=None, evalue=None, tb=None): + """Print internal error message (output defaults to stderr).""" + print(os.linesep, file=out) + print("""********** Oops, I did it again. ************* + +You have found an internal error in %(app)s. Please write a bug report +at %(url)s and include at least the information below: + +Not disclosing some of the information below due to privacy reasons is ok. +I will try to help you nonetheless, but you have to give me something +I can work with ;) . +""" % dict(app=AppName, url=SupportUrl), file=out) + if etype is None: + etype = sys.exc_info()[0] + if evalue is None: + evalue = sys.exc_info()[1] + print(etype, evalue, file=out) + if tb is None: + tb = sys.exc_info()[2] + traceback.print_exception(etype, evalue, tb, None, out) + print_app_info(out=out) + print_locale_info(out=out) + print(os.linesep, + "******** %s internal error, over and out ********" % AppName, file=out) + +def print_env_info(key, out=sys.stderr): + """If given environment key is defined, print it out.""" + value = os.getenv(key) + if value is not None: + print(key, "=", repr(value), file=out) + + +def print_locale_info(out=sys.stderr): + """Print locale info.""" + for key in ("LANGUAGE", "LC_ALL", "LC_CTYPE", "LANG"): + print_env_info(key, out=out) + + +def print_app_info(out=sys.stderr): + """Print system and application info (output defaults to stderr).""" print("System info:", file=out) - print("Python %s on %s" % (sys.version, sys.platform), file=out) + print(App, file=out) + print("Python %(version)s on %(platform)s" % + {"version": sys.version, "platform": sys.platform}, file=out) + stime = strtime(time.time()) + print("Local time:", stime, file=out) + print("sys.argv", sys.argv, file=out) + + +def strtime(t): + """Return ISO 8601 formatted time.""" + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t)) + \ + strtimezone() + + +def strtimezone(): + """Return timezone info, %z on some platforms, but not supported on all. + """ + if time.daylight: + zone = time.altzone + else: + zone = time.timezone + return "%+04d" % (-zone//3600) def p7zip_supports_rar (): @@ -480,17 +551,20 @@ def is_same_filename (filename1, filename2): return os.path.realpath(filename1) == os.path.realpath(filename2) -def link_or_copy(src, dst, verbose=False): +def link_or_copy(src, dst, verbosity=0): """Try to make a hard link from src to dst and if that fails copy the file. Hard links save some disk space and linking should fail fast since no copying is involved. """ - if verbose: + if verbosity > 0: log_info("Copying %s -> %s" % (src, dst)) try: os.link(src, dst) except OSError: - shutil.copy(src, dst) + try: + shutil.copy(src, dst) + except OSError as msg: + raise PatoolError(msg) def chdir(directory): diff --git a/setup.py b/setup.py index dda31e7..39ad8c0 100644 --- a/setup.py +++ b/setup.py @@ -309,7 +309,7 @@ installed. classifiers = [ 'Environment :: Console', 'Topic :: System :: Archiving', - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Programming Language :: Python', 'Operating System :: OS Independent', diff --git a/tests/__init__.py b/tests/__init__.py index dc4dab2..0fa69ae 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -21,6 +21,7 @@ import importlib basedir = os.path.dirname(__file__) datadir = os.path.join(basedir, 'data') +patool_cmd = os.path.join(os.path.dirname(basedir), "patool") # Python 3.x renamed the function name attribute if sys.version_info[0] > 2: diff --git a/tests/archives/__init__.py b/tests/archives/__init__.py index d3cca47..11cb281 100644 --- a/tests/archives/__init__.py +++ b/tests/archives/__init__.py @@ -67,15 +67,15 @@ class ArchiveTest (unittest.TestCase): self._archive_extract(archive, check) # archive name relative to tmpdir relarchive = os.path.join("..", archive[len(basedir)+1:]) - self._archive_extract(relarchive, check, verbose=True) + self._archive_extract(relarchive, check, verbosity=1) - def _archive_extract (self, archive, check, verbose=False): + def _archive_extract (self, archive, check, verbosity=0): # create a temporary directory for extraction tmpdir = patoolib.util.tmpdir(dir=basedir) try: olddir = patoolib.util.chdir(tmpdir) try: - output = patoolib._handle_archive(archive, 'extract', program=self.program, verbose=verbose) + output = patoolib.extract_archive(archive, program=self.program, verbosity=verbosity) if check: self.check_extracted_archive(archive, output, check) finally: @@ -103,10 +103,12 @@ class ArchiveTest (unittest.TestCase): self.check_textfile(txtfile2, 't2.txt') def check_directory (self, dirname, expectedname): + """Check that directory exists.""" self.assertTrue(os.path.isdir(dirname), dirname) self.assertEqual(os.path.basename(dirname), expectedname) def check_textfile (self, filename, expectedname): + """Check that filename exists and has the default content.""" self.assertTrue(os.path.isfile(filename), repr(filename)) self.assertEqual(os.path.basename(filename), expectedname) self.assertEqual(get_filecontent(filename), TextFileContent) @@ -114,14 +116,14 @@ class ArchiveTest (unittest.TestCase): def archive_list (self, filename): """Test archive listing.""" archive = os.path.join(datadir, filename) - patoolib._handle_archive(archive, 'list', program=self.program) - patoolib._handle_archive(archive, 'list', program=self.program, verbose=True) + for verbosity in (-1, 0, 1, 2): + patoolib.list_archive(archive, program=self.program, verbosity=verbosity) def archive_test (self, filename): """Test archive testing.""" archive = os.path.join(datadir, filename) - patoolib._handle_archive(archive, 'test', program=self.program) - patoolib._handle_archive(archive, 'test', program=self.program, verbose=True) + for verbosity in (-1, 0, 1, 2): + patoolib.test_archive(archive, program=self.program, verbosity=verbosity) def archive_create (self, archive, srcfiles=None, check=Content.Recursive): """Test archive creation.""" @@ -138,14 +140,13 @@ class ArchiveTest (unittest.TestCase): try: # The format and compression arguments are needed for creating # archives with unusual file extensions. - self._archive_create(archive, srcfiles, program=self.program) - # create again in verbose mode - self._archive_create(archive, srcfiles, program=self.program, verbose=True) + for verbosity in (-1, 0, 1, 2): + self._archive_create(archive, srcfiles, program=self.program, verbosity=verbosity) finally: if olddir: os.chdir(olddir) - def _archive_create (self, archive, srcfiles, **kwargs): + def _archive_create (self, archive, srcfiles, program=None, verbosity=0): """Create archive from filename.""" for srcfile in srcfiles: self.assertFalse(os.path.isabs(srcfile)) @@ -155,7 +156,7 @@ class ArchiveTest (unittest.TestCase): try: archive = os.path.join(tmpdir, archive) self.assertTrue(os.path.isabs(archive), "archive path is not absolute: %r" % archive) - patoolib._handle_archive(archive, 'create', *srcfiles, **kwargs) + patoolib.create_archive(archive, srcfiles, verbosity=verbosity, program=program) self.assertTrue(os.path.isfile(archive)) self.check_created_archive_with_test(archive) self.check_created_archive_with_diff(archive, srcfiles) @@ -163,7 +164,7 @@ class ArchiveTest (unittest.TestCase): shutil.rmtree(tmpdir) def check_created_archive_with_test(self, archive): - command = 'test' + command = patoolib.test_archive program = self.program # special case for programs that cannot test what they create if self.program in ('compress', 'py_gzip'): @@ -176,12 +177,12 @@ class ArchiveTest (unittest.TestCase): program = 'unzip' elif self.program in ('rzip', 'shorten'): program = 'py_echo' - command = 'list' + command = patoolib.list_archive elif self.program == 'lcab': program = 'cabextract' elif self.program == 'shar': return - patoolib._handle_archive(archive, command, program=program) + command(archive, program=program) def check_created_archive_with_diff(self, archive, srcfiles): """Extract created archive again and compare the contents.""" @@ -203,17 +204,15 @@ class ArchiveTest (unittest.TestCase): try: olddir = patoolib.util.chdir(tmpdir) try: - output = patoolib._handle_archive(archive, 'extract', program=program) + output = patoolib.extract_archive(archive, program=program) if len(srcfiles) == 1: source = os.path.join(datadir, srcfiles[0]) - res = patoolib.util.run([diff, "-urN", source, output]) - self.assertEqual(res, 0) + patoolib.util.run_checked([diff, "-urN", source, output]) else: for srcfile in srcfiles: source = os.path.join(datadir, srcfile) target = os.path.join(output, srcfile) - res = patoolib.util.run([diff, "-urN", source, target]) - self.assertEqual(res, 0) + patoolib.util.run_checked([diff, "-urN", source, target]) finally: if olddir: os.chdir(olddir) @@ -222,15 +221,17 @@ class ArchiveTest (unittest.TestCase): def get_filecontent(filename): + """Get file data as text.""" with open(filename) as fo: return fo.read() def get_nonexisting_directory(basedir): + """Note: this is _not_ intended to be used to create a directory.""" d = os.path.join(basedir, "foo") while os.path.exists(d): d += 'a' if len(d) > 100: # wtf - raise ValueError('could not find non-existing directory at %r' % basedir) + raise ValueError('could not construct unique directory name at %r' % basedir) return d diff --git a/tests/archives/test_bsdtar.py b/tests/archives/test_bsdtar.py index 855c3a4..753b3d7 100644 --- a/tests/archives/test_bsdtar.py +++ b/tests/archives/test_bsdtar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/archives/test_tar.py b/tests/archives/test_tar.py index 739e788..7e4da6c 100644 --- a/tests/archives/test_tar.py +++ b/tests/archives/test_tar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/archives/test_unadf.py b/tests/archives/test_unadf.py index 9a7e2ff..06aea11 100644 --- a/tests/archives/test_unadf.py +++ b/tests/archives/test_unadf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_baker.py b/tests/test_baker.py deleted file mode 100644 index ed85ab9..0000000 --- a/tests/test_baker.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2010-2013 Bastian Kleineidam -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -import unittest -from patoolib import baker - - -class TestBaker (unittest.TestCase): - - def test_func_args (self): - @baker.command - def func(a, b, c): - return (a, b, c) - argv = [__file__, 'func', '1', '2', '3'] - self.assertEqual(baker.test(argv=argv), "func('1','2','3')") - self.assertEqual(baker.run(argv=argv), ('1','2','3')) - - def test_func_noargs (self): - @baker.command - def func(): - return 42 - argv = [__file__, 'func'] - self.assertEqual(baker.test(argv=argv), "func()") - self.assertEqual(baker.run(argv=argv), 42) - - def test_func_kwargs (self): - @baker.command - def func(arg1, arg2, *args, **kwargs): - return arg1, arg2, kwargs['verbose'] - argv = [__file__, 'func', 'argvalue1', 'argvalue2', '--verbose'] - self.assertEqual(baker.test(argv=argv), "func('argvalue1','argvalue2',verbose=True)") - self.assertEqual(baker.run(argv=argv), ('argvalue1', 'argvalue2', True)) - - def test_func_kwargs_revorder (self): - @baker.command - def func(arg1, arg2, *args, **kwargs): - return arg1, arg2, kwargs['verbose'] - argv = [__file__, 'func', 'argvalue1', '--verbose', 'argvalue2'] - self.assertEqual(baker.test(argv=argv), "func('argvalue1','argvalue2',verbose=True)") - self.assertEqual(baker.run(argv=argv), ('argvalue1', 'argvalue2', True)) - - def test_func_kwargs_params (self): - @baker.command(shortopts={"verbose": "v"}, params={"verbose": "Be verbose"}) - def func(*args, **kwargs): - return kwargs['verbose'] - res = baker.run(argv=[__file__, 'func', '--verbose', 'arg1']) - self.assertEqual(res, True) - res = baker.run(argv=[__file__, 'func', 'arg1', '--verbose']) - self.assertEqual(res, True) - res = baker.run(argv=[__file__, 'func', '-v']) - self.assertEqual(res, True) - res = baker.run(argv=[__file__, 'func', '-v', 'arg1']) - self.assertEqual(res, True) diff --git a/tests/test_create.py b/tests/test_create.py new file mode 100644 index 0000000..3af460f --- /dev/null +++ b/tests/test_create.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest +import os +import sys +import shutil +from patoolib import util +from . import basedir, datadir, needs_program, patool_cmd + +class ArchiveCreateTest (unittest.TestCase): + + @needs_program('7z') + def test_create(self): + tmpdir = util.tmpdir(dir=basedir) + try: + files = [os.path.join(datadir, "t"), os.path.join(datadir, "t.txt")] + archive = os.path.join(tmpdir, "t.7z") + cmd = [sys.executable, patool_cmd, "-vv", "create", archive] + cmd.extend(files) + util.run_checked(cmd) + finally: + shutil.rmtree(tmpdir) diff --git a/tests/test_diff.py b/tests/test_diff.py index 6b1072c..e88cb73 100644 --- a/tests/test_diff.py +++ b/tests/test_diff.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Bastian Kleineidam +# Copyright (C) 2010-2013 Bastian Kleineidam # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,8 +15,9 @@ # along with this program. If not, see . import unittest import os -import patoolib -from . import datadir, needs_program +import sys +from patoolib.util import run_checked +from . import datadir, needs_program, patool_cmd class ArchiveDiffTest (unittest.TestCase): @@ -26,6 +27,4 @@ class ArchiveDiffTest (unittest.TestCase): def test_diff (self): archive1 = os.path.join(datadir, "t.tar") archive2 = os.path.join(datadir, "t.zip") - res = patoolib.handle_archive(archive1, "diff", archive2) - # both archives have the same data - self.assertEqual(res, 0) + run_checked([sys.executable, patool_cmd, "-vv", "diff", archive1, archive2]) diff --git a/tests/test_extract.py b/tests/test_extract.py new file mode 100644 index 0000000..b3695b9 --- /dev/null +++ b/tests/test_extract.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest +import os +import sys +import shutil +from patoolib import util +from . import basedir, datadir, needs_program, patool_cmd + +class ArchiveExtractTest (unittest.TestCase): + + @needs_program('7z') + def test_extract(self): + tmpdir = util.tmpdir(dir=basedir) + try: + archive = os.path.join(datadir, "t .7z") + util.run_checked([sys.executable, patool_cmd, "-vv", "extract", "--outdir", tmpdir, archive]) + finally: + shutil.rmtree(tmpdir) diff --git a/tests/test_formats.py b/tests/test_formats.py index d50eef9..20e4ec5 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -14,9 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import unittest -import patoolib +import sys +from patoolib.util import run_checked +from . import patool_cmd class TestFormats (unittest.TestCase): def test_list_formats (self): - patoolib.list_formats() + run_checked([sys.executable, patool_cmd, "-vv", 'formats']) diff --git a/tests/test_list.py b/tests/test_list.py new file mode 100644 index 0000000..5198830 --- /dev/null +++ b/tests/test_list.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest +import os +import sys +from patoolib.util import run_checked +from . import datadir, needs_program, patool_cmd + +class ArchiveListTest (unittest.TestCase): + + @needs_program('tar') + def test_list(self): + archive = os.path.join(datadir, "t.tar") + run_checked([sys.executable, patool_cmd, "-vv", "list", archive]) diff --git a/tests/test_repack.py b/tests/test_repack.py index 0b35e8c..88f62f9 100644 --- a/tests/test_repack.py +++ b/tests/test_repack.py @@ -15,23 +15,21 @@ # along with this program. If not, see . import unittest import os +import sys import shutil -import patoolib -from . import datadir, needs_program, needs_one_program +from patoolib import util +from . import datadir, needs_program, needs_one_program, patool_cmd class ArchiveRepackTest (unittest.TestCase): def repack(self, name1, name2): """Repack archive with name1 to archive with name2.""" archive1 = os.path.join(datadir, name1) - tmpdir = patoolib.util.tmpdir() + tmpdir = util.tmpdir() try: archive2 = os.path.join(tmpdir, name2) - res = patoolib.handle_archive(archive1, "repack", archive2) - self.assertEqual(res, 0) - res = patoolib.handle_archive(archive1, "diff", archive2) - # both archives have the same data - self.assertEqual(res, 0) + util.run_checked([sys.executable, patool_cmd, "-vv", "repack", archive1, archive2]) + util.run_checked([sys.executable, patool_cmd, "diff", archive1, archive2]) finally: shutil.rmtree(tmpdir) diff --git a/tests/test_search.py b/tests/test_search.py index 586a4cf..904d060 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -15,8 +15,9 @@ # along with this program. If not, see . import unittest import os -import patoolib -from . import datadir, needs_program +import sys +from patoolib.util import run_checked +from . import datadir, needs_program, patool_cmd class ArchiveSearchTest (unittest.TestCase): @@ -25,7 +26,10 @@ class ArchiveSearchTest (unittest.TestCase): @needs_program('tar') def test_search(self): pattern = "42" - archive1 = os.path.join(datadir, "t.tar") - archive2 = os.path.join(datadir, "t.zip") - res = patoolib.handle_archive(pattern, "search", archive1, archive2) - self.assertEqual(res, 0) + archive = os.path.join(datadir, "t.tar") + self.search(pattern, archive) + archive = os.path.join(datadir, "t.zip") + self.search(pattern, archive) + + def search(self, pattern, archive): + run_checked([sys.executable, patool_cmd, "-vv", "search", pattern, archive]) diff --git a/tests/test_test.py b/tests/test_test.py new file mode 100644 index 0000000..8e42d01 --- /dev/null +++ b/tests/test_test.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest +import os +import sys +from patoolib.util import run_checked +from . import datadir, needs_program, patool_cmd + +class ArchiveTestTest (unittest.TestCase): + + @needs_program('7z') + def test_test(self): + archive = os.path.join(datadir, "t .7z") + run_checked([sys.executable, patool_cmd, "test", archive])