diff options
author | Michał Górny <mgorny@gentoo.org> | 2018-02-09 23:51:48 +0100 |
---|---|---|
committer | Michał Górny <mgorny@gentoo.org> | 2018-02-09 23:51:48 +0100 |
commit | 21f2da234216a4413d4060ae7442345f519cff8c (patch) | |
tree | a1685b56cb65aed2ffc3ddb8a1fce81f1afdcdf3 | |
parent | 275325d1f9a8a2ffa62f31f46d602e3765d40ad8 (diff) | |
download | gemato-21f2da234216a4413d4060ae7442345f519cff8c.tar.gz |
cli: Start abstracting out commands into objects
-rw-r--r-- | gemato/cli.py | 640 |
1 files changed, 343 insertions, 297 deletions
diff --git a/gemato/cli.py b/gemato/cli.py index 60ceeee..07dc534 100644 --- a/gemato/cli.py +++ b/gemato/cli.py @@ -26,227 +26,354 @@ def verify_failure(e): return False -def do_verify(args, argp): - ret = True - - for p in args.paths: - tlm = gemato.find_top_level.find_top_level_manifest(p) - if tlm is None: - logging.error('Top-level Manifest not found in {}'.format(p)) - return 1 - - init_kwargs = {} - kwargs = {} - if args.jobs is not None: - if args.jobs < 1: - argp.error('--jobs must be positive') - init_kwargs['max_jobs'] = args.jobs - if args.keep_going: - kwargs['fail_handler'] = verify_failure - if not args.openpgp_verify: - init_kwargs['verify_openpgp'] = False - - # use isolated environment if key is specified; - # system environment otherwise - if args.openpgp_key is not None: - env_class = gemato.openpgp.OpenPGPEnvironment - else: - env_class = gemato.openpgp.OpenPGPSystemEnvironment - - with env_class() as env: - if args.openpgp_key is not None: - with io.open(args.openpgp_key, 'rb') as f: - env.import_key(f) - # always refresh keys to check for revocation - # (unless user specifically asked us not to) - if args.refresh_keys: - logging.info('Refreshing keys from keyserver...') - env.refresh_keys() - logging.info('Keys refreshed.') - init_kwargs['openpgp_env'] = env - - start = timeit.default_timer() - m = gemato.recursiveloader.ManifestRecursiveLoader(tlm, **init_kwargs) - if args.require_signed_manifest and not m.openpgp_signed: - logging.error('Top-level Manifest {} is not OpenPGP signed'.format(tlm)) +class GematoCommand(object): + """ + Base class for commands supported by gemato. + """ + + @property + def name(self): + """ + Command name. Used on the command-line + """ + pass + + @property + def help(self): + """ + Command description for --help. + """ + pass + + def add_options(self, subp): + """ + Add options specific to the command to subparser @subp. + """ + pass + + def __call__(self, args, argp): + """ + Perform the command, given command-line arguments @args. + @argp is argparse instance provided for error reporting. + """ + pass + + +class VerifyCommand(GematoCommand): + name = 'verify' + help = 'Verify one or more directories against Manifests' + + def add_options(self, verify): + verify.add_argument('paths', nargs='*', default=['.'], + help='Paths to verify (defaults to "." if none specified)') + verify.add_argument('-j', '--jobs', type=int, + help='Specify the maximum number of parallel jobs to use (default: {})' + .format(multiprocessing.cpu_count())) + verify.add_argument('-k', '--keep-going', action='store_true', + help='Continue reporting errors rather than terminating on the first failure') + verify.add_argument('-K', '--openpgp-key', + help='Use only the OpenPGP key(s) from a specific file') + verify.add_argument('-P', '--no-openpgp-verify', action='store_false', + dest='openpgp_verify', + help='Disable OpenPGP verification of signed Manifests') + verify.add_argument('-R', '--no-refresh-keys', action='store_false', + dest='refresh_keys', + help='Disable refreshing OpenPGP key (prevents network access, applicable ' + +'when using -K only)') + verify.add_argument('-s', '--require-signed-manifest', action='store_true', + help='Require that the top-level Manifest is OpenPGP signed') + + def __call__(self, args, argp): + ret = True + + for p in args.paths: + tlm = gemato.find_top_level.find_top_level_manifest(p) + if tlm is None: + logging.error('Top-level Manifest not found in {}'.format(p)) return 1 - ts = m.find_timestamp() - if ts: - logging.info('Manifest timestamp: {} UTC'.format(ts.ts)) - - if m.openpgp_signed: - logging.info('Valid OpenPGP signature found:') - logging.info('- primary key: {}'.format( - m.openpgp_signature.primary_key_fingerprint)) - logging.info('- subkey: {}'.format( - m.openpgp_signature.fingerprint)) - logging.info('- timestamp: {} UTC'.format( - m.openpgp_signature.timestamp)) - - logging.info('Verifying {}...'.format(p)) - - relpath = os.path.relpath(p, os.path.dirname(tlm)) - if relpath == '.': - relpath = '' - ret &= m.assert_directory_verifies(relpath, **kwargs) - - stop = timeit.default_timer() - logging.info('{} verified in {:.2f} seconds'.format(p, stop - start)) - return 0 if ret else 1 - - -def do_update(args, argp): - for p in args.paths: - tlm = gemato.find_top_level.find_top_level_manifest(p) - if tlm is None: - logging.error('Top-level Manifest not found in {}'.format(p)) - return 1 - - init_kwargs = {} - save_kwargs = {} - update_kwargs = {} - if args.hashes is not None: - init_kwargs['hashes'] = args.hashes.split() - if args.compress_watermark is not None: - if args.compress_watermark < 0: - argp.error('--compress-watermark must not be negative!') - init_kwargs['compress_watermark'] = args.compress_watermark - if args.compress_format is not None: - init_kwargs['compress_format'] = args.compress_format - if args.force_rewrite: - save_kwargs['force'] = True - if args.jobs is not None: - if args.jobs < 1: - argp.error('--jobs must be positive') - init_kwargs['max_jobs'] = args.jobs - if args.openpgp_id is not None: - init_kwargs['openpgp_keyid'] = args.openpgp_id - if args.profile is not None: - init_kwargs['profile'] = gemato.profile.get_profile_by_name( - args.profile) - if args.sign is not None: - init_kwargs['sign_openpgp'] = args.sign - - # use isolated environment if key is specified; - # system environment otherwise - if args.openpgp_key is not None: - env_class = gemato.openpgp.OpenPGPEnvironment - else: - env_class = gemato.openpgp.OpenPGPSystemEnvironment - - with env_class() as env: + init_kwargs = {} + kwargs = {} + if args.jobs is not None: + if args.jobs < 1: + argp.error('--jobs must be positive') + init_kwargs['max_jobs'] = args.jobs + if args.keep_going: + kwargs['fail_handler'] = verify_failure + if not args.openpgp_verify: + init_kwargs['verify_openpgp'] = False + + # use isolated environment if key is specified; + # system environment otherwise if args.openpgp_key is not None: - with io.open(args.openpgp_key, 'rb') as f: - env.import_key(f) - init_kwargs['openpgp_env'] = env - - start = timeit.default_timer() - m = gemato.recursiveloader.ManifestRecursiveLoader(tlm, - **init_kwargs) - - # if not specified by user, profile must set it - if m.hashes is None: - argp.error('--hashes must be specified if not implied by --profile') - - relpath = os.path.relpath(p, os.path.dirname(tlm)) - if relpath == '.': - relpath = '' - if args.timestamp and relpath != '': - argp.error('Timestamp can only be updated if doing full-tree update') - if args.incremental: - if relpath != '': - argp.error('Incremental works only for full-tree update') - last_ts = m.find_timestamp() - if last_ts is None: - argp.error('Incremental specified but no timestamp in Manifest') - update_kwargs['last_mtime'] = last_ts.ts.timestamp() - - logging.info('Updating Manifests in {}...'.format(p)) - - start_ts = datetime.datetime.utcnow() - m.update_entries_for_directory(relpath, **update_kwargs) - - # write TIMESTAMP if requested, or if already there - if relpath != '': - # skip timestamp if not doing full update - pass - elif args.timestamp: - m.set_timestamp(start_ts) + env_class = gemato.openpgp.OpenPGPEnvironment else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: + if args.openpgp_key is not None: + with io.open(args.openpgp_key, 'rb') as f: + env.import_key(f) + # always refresh keys to check for revocation + # (unless user specifically asked us not to) + if args.refresh_keys: + logging.info('Refreshing keys from keyserver...') + env.refresh_keys() + logging.info('Keys refreshed.') + init_kwargs['openpgp_env'] = env + + start = timeit.default_timer() + m = gemato.recursiveloader.ManifestRecursiveLoader(tlm, **init_kwargs) + if args.require_signed_manifest and not m.openpgp_signed: + logging.error('Top-level Manifest {} is not OpenPGP signed'.format(tlm)) + return 1 + ts = m.find_timestamp() - if ts is not None: - ts.ts = start_ts - - m.save_manifests(**save_kwargs) - - stop = timeit.default_timer() - logging.info('{} updated in {:.2f} seconds'.format(p, stop - start)) - return 0 - - -def do_create(args, argp): - for p in args.paths: - init_kwargs = {} - save_kwargs = {} - init_kwargs['allow_create'] = True - if args.hashes is not None: - init_kwargs['hashes'] = args.hashes.split() - if args.compress_watermark is not None: - if args.compress_watermark < 0: - argp.error('--compress-watermark must not be negative!') - init_kwargs['compress_watermark'] = args.compress_watermark - if args.compress_format is not None: - init_kwargs['compress_format'] = args.compress_format - if args.force_rewrite: - save_kwargs['force'] = True - if args.jobs is not None: - if args.jobs < 1: - argp.error('--jobs must be positive') - init_kwargs['max_jobs'] = args.jobs - if args.openpgp_id is not None: - init_kwargs['openpgp_keyid'] = args.openpgp_id - if args.profile is not None: - init_kwargs['profile'] = gemato.profile.get_profile_by_name( - args.profile) - if args.sign is not None: - init_kwargs['sign_openpgp'] = args.sign - - # use isolated environment if key is specified; - # system environment otherwise - if args.openpgp_key is not None: - env_class = gemato.openpgp.OpenPGPEnvironment - else: - env_class = gemato.openpgp.OpenPGPSystemEnvironment - - with env_class() as env: + if ts: + logging.info('Manifest timestamp: {} UTC'.format(ts.ts)) + + if m.openpgp_signed: + logging.info('Valid OpenPGP signature found:') + logging.info('- primary key: {}'.format( + m.openpgp_signature.primary_key_fingerprint)) + logging.info('- subkey: {}'.format( + m.openpgp_signature.fingerprint)) + logging.info('- timestamp: {} UTC'.format( + m.openpgp_signature.timestamp)) + + logging.info('Verifying {}...'.format(p)) + + relpath = os.path.relpath(p, os.path.dirname(tlm)) + if relpath == '.': + relpath = '' + ret &= m.assert_directory_verifies(relpath, **kwargs) + + stop = timeit.default_timer() + logging.info('{} verified in {:.2f} seconds'.format(p, stop - start)) + return 0 if ret else 1 + + +class UpdateCommand(GematoCommand): + name = 'update' + help = 'Update the Manifest entries for one or more directory trees' + + def add_options(self, update): + update.add_argument('paths', nargs='*', default=['.'], + help='Paths to update (defaults to "." if none specified)') + update.add_argument('-c', '--compress-watermark', type=int, + help='Minimum Manifest size for files to be compressed') + update.add_argument('-C', '--compress-format', + help='Format for compressed files (e.g. "gz", "bz2"...)') + update.add_argument('-f', '--force-rewrite', action='store_true', + help='Force rewriting all the Manifests, even if they did not change') + update.add_argument('-H', '--hashes', + help='Whitespace-separated list of hashes to use') + update.add_argument('-i', '--incremental', action='store_true', + help='Perform incremental update by comparing mtimes against TIMESTAMP') + update.add_argument('-j', '--jobs', type=int, + help='Specify the maximum number of parallel jobs to use (default: {})' + .format(multiprocessing.cpu_count())) + update.add_argument('-k', '--openpgp-id', + help='Use the specified OpenPGP key (by ID or user)') + update.add_argument('-K', '--openpgp-key', + help='Use only the OpenPGP key(s) from a specific file') + update.add_argument('-p', '--profile', + help='Use the specified profile ("default", "ebuild", "old-ebuild"...)') + signgroup = update.add_mutually_exclusive_group() + signgroup.add_argument('-s', '--sign', action='store_true', + default=None, + help='Force signing the top-level Manifest') + signgroup.add_argument('-S', '--no-sign', action='store_false', + dest='sign', + help='Disable signing the top-level Manifest') + update.add_argument('-t', '--timestamp', action='store_true', + help='Include TIMESTAMP entry in Manifest') + + def __call__(self, args, argp): + for p in args.paths: + tlm = gemato.find_top_level.find_top_level_manifest(p) + if tlm is None: + logging.error('Top-level Manifest not found in {}'.format(p)) + return 1 + + init_kwargs = {} + save_kwargs = {} + update_kwargs = {} + if args.hashes is not None: + init_kwargs['hashes'] = args.hashes.split() + if args.compress_watermark is not None: + if args.compress_watermark < 0: + argp.error('--compress-watermark must not be negative!') + init_kwargs['compress_watermark'] = args.compress_watermark + if args.compress_format is not None: + init_kwargs['compress_format'] = args.compress_format + if args.force_rewrite: + save_kwargs['force'] = True + if args.jobs is not None: + if args.jobs < 1: + argp.error('--jobs must be positive') + init_kwargs['max_jobs'] = args.jobs + if args.openpgp_id is not None: + init_kwargs['openpgp_keyid'] = args.openpgp_id + if args.profile is not None: + init_kwargs['profile'] = gemato.profile.get_profile_by_name( + args.profile) + if args.sign is not None: + init_kwargs['sign_openpgp'] = args.sign + + # use isolated environment if key is specified; + # system environment otherwise if args.openpgp_key is not None: - with io.open(args.openpgp_key, 'rb') as f: - env.import_key(f) - init_kwargs['openpgp_env'] = env + env_class = gemato.openpgp.OpenPGPEnvironment + else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: + if args.openpgp_key is not None: + with io.open(args.openpgp_key, 'rb') as f: + env.import_key(f) + init_kwargs['openpgp_env'] = env + + start = timeit.default_timer() + m = gemato.recursiveloader.ManifestRecursiveLoader(tlm, + **init_kwargs) + + # if not specified by user, profile must set it + if m.hashes is None: + argp.error('--hashes must be specified if not implied by --profile') + + relpath = os.path.relpath(p, os.path.dirname(tlm)) + if relpath == '.': + relpath = '' + if args.timestamp and relpath != '': + argp.error('Timestamp can only be updated if doing full-tree update') + if args.incremental: + if relpath != '': + argp.error('Incremental works only for full-tree update') + last_ts = m.find_timestamp() + if last_ts is None: + argp.error('Incremental specified but no timestamp in Manifest') + update_kwargs['last_mtime'] = last_ts.ts.timestamp() + + logging.info('Updating Manifests in {}...'.format(p)) + + start_ts = datetime.datetime.utcnow() + m.update_entries_for_directory(relpath, **update_kwargs) + + # write TIMESTAMP if requested, or if already there + if relpath != '': + # skip timestamp if not doing full update + pass + elif args.timestamp: + m.set_timestamp(start_ts) + else: + ts = m.find_timestamp() + if ts is not None: + ts.ts = start_ts + + m.save_manifests(**save_kwargs) + + stop = timeit.default_timer() + logging.info('{} updated in {:.2f} seconds'.format(p, stop - start)) + return 0 + + +class CreateCommand(GematoCommand): + name = 'create' + help = 'Create a Manifest tree starting at the specified file' + + def add_options(self, create): + create.add_argument('paths', nargs='*', default=['.'], + help='Paths to create (defaults to "Manifest" if none specified)') + create.add_argument('-c', '--compress-watermark', type=int, + help='Minimum Manifest size for files to be compressed') + create.add_argument('-C', '--compress-format', + help='Format for compressed files (e.g. "gz", "bz2"...)') + create.add_argument('-f', '--force-rewrite', action='store_true', + help='Force rewriting all the Manifests, even if they did not change') + create.add_argument('-H', '--hashes', + help='Whitespace-separated list of hashes to use') + create.add_argument('-j', '--jobs', type=int, + help='Specify the maximum number of parallel jobs to use (default: {})' + .format(multiprocessing.cpu_count())) + create.add_argument('-k', '--openpgp-id', + help='Use the specified OpenPGP key (by ID or user)') + create.add_argument('-K', '--openpgp-key', + help='Use only the OpenPGP key(s) from a specific file') + create.add_argument('-p', '--profile', + help='Use the specified profile ("default", "ebuild", "old-ebuild"...)') + signgroup = create.add_mutually_exclusive_group() + signgroup.add_argument('-s', '--sign', action='store_true', + default=None, + help='Force signing the top-level Manifest') + signgroup.add_argument('-S', '--no-sign', action='store_false', + dest='sign', + help='Disable signing the top-level Manifest') + create.add_argument('-t', '--timestamp', action='store_true', + help='Include TIMESTAMP entry in Manifest') + + def __call__(self, args, argp): + for p in args.paths: + init_kwargs = {} + save_kwargs = {} + init_kwargs['allow_create'] = True + if args.hashes is not None: + init_kwargs['hashes'] = args.hashes.split() + if args.compress_watermark is not None: + if args.compress_watermark < 0: + argp.error('--compress-watermark must not be negative!') + init_kwargs['compress_watermark'] = args.compress_watermark + if args.compress_format is not None: + init_kwargs['compress_format'] = args.compress_format + if args.force_rewrite: + save_kwargs['force'] = True + if args.jobs is not None: + if args.jobs < 1: + argp.error('--jobs must be positive') + init_kwargs['max_jobs'] = args.jobs + if args.openpgp_id is not None: + init_kwargs['openpgp_keyid'] = args.openpgp_id + if args.profile is not None: + init_kwargs['profile'] = gemato.profile.get_profile_by_name( + args.profile) + if args.sign is not None: + init_kwargs['sign_openpgp'] = args.sign + + # use isolated environment if key is specified; + # system environment otherwise + if args.openpgp_key is not None: + env_class = gemato.openpgp.OpenPGPEnvironment + else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: + if args.openpgp_key is not None: + with io.open(args.openpgp_key, 'rb') as f: + env.import_key(f) + init_kwargs['openpgp_env'] = env - start = timeit.default_timer() - m = gemato.recursiveloader.ManifestRecursiveLoader( - os.path.join(p, 'Manifest'), **init_kwargs) + start = timeit.default_timer() + m = gemato.recursiveloader.ManifestRecursiveLoader( + os.path.join(p, 'Manifest'), **init_kwargs) - # if not specified by user, profile must set it - if m.hashes is None: - argp.error('--hashes must be specified if not implied by --profile') + # if not specified by user, profile must set it + if m.hashes is None: + argp.error('--hashes must be specified if not implied by --profile') - logging.info('Creating Manifests in {}...'.format(p)) + logging.info('Creating Manifests in {}...'.format(p)) - start_ts = datetime.datetime.utcnow() - m.update_entries_for_directory() + start_ts = datetime.datetime.utcnow() + m.update_entries_for_directory() - # write TIMESTAMP if requested, or if already there - if args.timestamp: - m.set_timestamp(start_ts) + # write TIMESTAMP if requested, or if already there + if args.timestamp: + m.set_timestamp(start_ts) - m.save_manifests(**save_kwargs) + m.save_manifests(**save_kwargs) - stop = timeit.default_timer() - logging.info('{} updated in {:.2f} seconds'.format(p, stop - start)) - return 0 + stop = timeit.default_timer() + logging.info('{} updated in {:.2f} seconds'.format(p, stop - start)) + return 0 def main(argv): @@ -255,99 +382,18 @@ def main(argv): description='Gentoo Manifest Tool') subp = argp.add_subparsers() - verify = subp.add_parser('verify', - help='Verify one or more directories against Manifests') - verify.add_argument('paths', nargs='*', default=['.'], - help='Paths to verify (defaults to "." if none specified)') - verify.add_argument('-j', '--jobs', type=int, - help='Specify the maximum number of parallel jobs to use (default: {})' - .format(multiprocessing.cpu_count())) - verify.add_argument('-k', '--keep-going', action='store_true', - help='Continue reporting errors rather than terminating on the first failure') - verify.add_argument('-K', '--openpgp-key', - help='Use only the OpenPGP key(s) from a specific file') - verify.add_argument('-P', '--no-openpgp-verify', action='store_false', - dest='openpgp_verify', - help='Disable OpenPGP verification of signed Manifests') - verify.add_argument('-R', '--no-refresh-keys', action='store_false', - dest='refresh_keys', - help='Disable refreshing OpenPGP key (prevents network access, applicable ' - +'when using -K only)') - verify.add_argument('-s', '--require-signed-manifest', action='store_true', - help='Require that the top-level Manifest is OpenPGP signed') - verify.set_defaults(func=do_verify) - - update = subp.add_parser('update', - help='Update the Manifest entries for one or more directory trees') - update.add_argument('paths', nargs='*', default=['.'], - help='Paths to update (defaults to "." if none specified)') - update.add_argument('-c', '--compress-watermark', type=int, - help='Minimum Manifest size for files to be compressed') - update.add_argument('-C', '--compress-format', - help='Format for compressed files (e.g. "gz", "bz2"...)') - update.add_argument('-f', '--force-rewrite', action='store_true', - help='Force rewriting all the Manifests, even if they did not change') - update.add_argument('-H', '--hashes', - help='Whitespace-separated list of hashes to use') - update.add_argument('-i', '--incremental', action='store_true', - help='Perform incremental update by comparing mtimes against TIMESTAMP') - update.add_argument('-j', '--jobs', type=int, - help='Specify the maximum number of parallel jobs to use (default: {})' - .format(multiprocessing.cpu_count())) - update.add_argument('-k', '--openpgp-id', - help='Use the specified OpenPGP key (by ID or user)') - update.add_argument('-K', '--openpgp-key', - help='Use only the OpenPGP key(s) from a specific file') - update.add_argument('-p', '--profile', - help='Use the specified profile ("default", "ebuild", "old-ebuild"...)') - signgroup = update.add_mutually_exclusive_group() - signgroup.add_argument('-s', '--sign', action='store_true', - default=None, - help='Force signing the top-level Manifest') - signgroup.add_argument('-S', '--no-sign', action='store_false', - dest='sign', - help='Disable signing the top-level Manifest') - update.add_argument('-t', '--timestamp', action='store_true', - help='Include TIMESTAMP entry in Manifest') - update.set_defaults(func=do_update) - - create = subp.add_parser('create', - help='Create a Manifest tree starting at the specified file') - create.add_argument('paths', nargs='*', default=['.'], - help='Paths to create (defaults to "Manifest" if none specified)') - create.add_argument('-c', '--compress-watermark', type=int, - help='Minimum Manifest size for files to be compressed') - create.add_argument('-C', '--compress-format', - help='Format for compressed files (e.g. "gz", "bz2"...)') - create.add_argument('-f', '--force-rewrite', action='store_true', - help='Force rewriting all the Manifests, even if they did not change') - create.add_argument('-H', '--hashes', - help='Whitespace-separated list of hashes to use') - create.add_argument('-j', '--jobs', type=int, - help='Specify the maximum number of parallel jobs to use (default: {})' - .format(multiprocessing.cpu_count())) - create.add_argument('-k', '--openpgp-id', - help='Use the specified OpenPGP key (by ID or user)') - create.add_argument('-K', '--openpgp-key', - help='Use only the OpenPGP key(s) from a specific file') - create.add_argument('-p', '--profile', - help='Use the specified profile ("default", "ebuild", "old-ebuild"...)') - signgroup = create.add_mutually_exclusive_group() - signgroup.add_argument('-s', '--sign', action='store_true', - default=None, - help='Force signing the top-level Manifest') - signgroup.add_argument('-S', '--no-sign', action='store_false', - dest='sign', - help='Disable signing the top-level Manifest') - create.add_argument('-t', '--timestamp', action='store_true', - help='Include TIMESTAMP entry in Manifest') - create.set_defaults(func=do_create) + commands = [VerifyCommand, UpdateCommand, CreateCommand] + for cmdclass in commands: + cmd = cmdclass() + cmdp = subp.add_parser(cmd.name, help=cmd.help) + cmd.add_options(cmdp) + cmdp.set_defaults(cmd=cmd) vals = argp.parse_args(argv[1:]) - if not hasattr(vals, 'func'): + if not hasattr(vals, 'cmd'): argp.error('No function specified') try: - return vals.func(vals, argp) + return vals.cmd(vals, argp) except gemato.exceptions.GematoException as e: logging.error(str(e)) return 1 |