Replace baker with argparse; some code restructuration.

This commit is contained in:
Bastian Kleineidam 2013-02-25 21:04:02 +01:00
parent a905c893b9
commit cee88ecb60
57 changed files with 771 additions and 1245 deletions

View File

@ -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.

View File

@ -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

View File

@ -6,46 +6,47 @@ NAME
patool - portable command line archive file manager
SYNOPSIS
patool (list|test) [--verbose] <archive-file>...
patool extract [--verbose] [--outdir=DIRNAME] <archive-
file>...
patool create [--verbose] <archive-file> [files...]
patool diff [--verbose] <archive1> <archive2>
patool search [--verbose] <pattern> <archive-file>...
patool repack [--verbose] <archive1> <archive2>
patool formats
patool [global-options]
(list|test|extract|create|diff|search|repack|formats)
[command-options] <command-arguments>...
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] <archive>...
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 <archive>...
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 <archive> <file-or-directory>...
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 <archive>...
Test the given archives. If the helper application does not
support testing, the archive contents are listed instead.
diff
patool diff <archive1> <archive2>
Show differences between two archives with the diff(1) program.
The diff options used are -urN.
search
patool search <pattern> <archive>
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 <archive> <archive_new>
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).

182
patool
View File

@ -15,93 +15,149 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
patool [extract|list|create|formats] [sub-command-options] <command-args>
patool [global-options] {extract|list|create|diff|search|formats} [sub-command-options] <command-args>
"""
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())

View File

@ -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

View File

@ -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 <options>\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 <command> --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

View File

@ -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])

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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])

View File

@ -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

View File

@ -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])

View File

@ -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']

View File

@ -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')

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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'

View File

@ -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 <http://www.gnu.org/licenses/>.
"""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

View File

@ -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

View File

@ -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

View File

@ -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})

View File

@ -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 <http://www.gnu.org/licenses/>.
"""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

View File

@ -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})

View File

@ -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

View File

@ -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')

View File

@ -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]

View File

@ -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), '>',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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',

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
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)

35
tests/test_create.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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)

View File

@ -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 <http://www.gnu.org/licenses/>.
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])

32
tests/test_extract.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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)

View File

@ -14,9 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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'])

27
tests/test_list.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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])

View File

@ -15,23 +15,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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)

View File

@ -15,8 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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])

27
tests/test_test.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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])