diff --git a/swiftclient/shell.py b/swiftclient/shell.py index 06c37b3e..e3b08beb 100755 --- a/swiftclient/shell.py +++ b/swiftclient/shell.py @@ -101,13 +101,13 @@ def st_delete(parser, args, output_manager): 'Its value must be a positive integer. Default is 10.') (options, args) = parse_args(parser, args) args = args[1:] - if (not args and not options.yes_all) or (args and options.yes_all): + if (not args and not options['yes_all']) or (args and options['yes_all']): output_manager.error('Usage: %s delete %s\n%s', BASENAME, st_delete_options, st_delete_help) return - if options.object_threads <= 0: + if options['object_threads'] <= 0: output_manager.error( 'ERROR: option --object-threads should be a positive integer.' '\n\nUsage: %s delete %s\n%s', @@ -115,7 +115,7 @@ def st_delete(parser, args, output_manager): st_delete_help) return - if options.container_threads <= 0: + if options['container_threads'] <= 0: output_manager.error( 'ERROR: option --container-threads should be a positive integer.' '\n\nUsage: %s delete %s\n%s', @@ -123,9 +123,8 @@ def st_delete(parser, args, output_manager): st_delete_help) return - _opts = vars(options) - _opts['object_dd_threads'] = options.object_threads - with SwiftService(options=_opts) as swift: + options['object_dd_threads'] = options['object_threads'] + with SwiftService(options=options) as swift: try: if not args: del_iter = swift.delete() @@ -169,7 +168,7 @@ def st_delete(parser, args, output_manager): pass for o in objs: - if options.yes_all: + if options['yes_all']: p = '{0}/{1}'.format(c, o) else: p = o @@ -180,12 +179,12 @@ def st_delete(parser, args, output_manager): .format(c, o, r['error'])) else: if r['success']: - if options.verbose: + if options['verbose']: a = (' [after {0} attempts]'.format(a) if a > 1 else '') if r['action'] == 'delete_object': - if options.yes_all: + if options['yes_all']: p = '{0}/{1}'.format(c, o) else: p = o @@ -319,40 +318,39 @@ def st_download(parser, args, output_manager): 'are listed in the object store.') (options, args) = parse_args(parser, args) args = args[1:] - if options.out_file == '-': - options.verbose = 0 + if options['out_file'] == '-': + options['verbose'] = 0 - if options.out_file and len(args) != 2: + if options['out_file'] and len(args) != 2: exit('-o option only allowed for single file downloads') - if not options.prefix: - options.remove_prefix = False + if not options['prefix']: + options['remove_prefix'] = False - if options.out_directory and len(args) == 2: + if options['out_directory'] and len(args) == 2: exit('Please use -o option for single file downloads and renames') - if (not args and not options.yes_all) or (args and options.yes_all): + if (not args and not options['yes_all']) or (args and options['yes_all']): output_manager.error('Usage: %s download %s\n%s', BASENAME, st_download_options, st_download_help) return - if options.object_threads <= 0: + if options['object_threads'] <= 0: output_manager.error( 'ERROR: option --object-threads should be a positive integer.\n\n' 'Usage: %s download %s\n%s', BASENAME, st_download_options, st_download_help) return - if options.container_threads <= 0: + if options['container_threads'] <= 0: output_manager.error( 'ERROR: option --container-threads should be a positive integer.' '\n\nUsage: %s download %s\n%s', BASENAME, st_download_options, st_download_help) return - _opts = vars(options) - _opts['object_dd_threads'] = options.object_threads - with SwiftService(options=_opts) as swift: + options['object_dd_threads'] = options['object_threads'] + with SwiftService(options=options) as swift: try: if not args: down_iter = swift.download() @@ -372,13 +370,13 @@ def st_download(parser, args, output_manager): down_iter = swift.download(container, objects) for down in down_iter: - if options.out_file == '-' and 'contents' in down: + if options['out_file'] == '-' and 'contents' in down: contents = down['contents'] for chunk in contents: output_manager.print_raw(chunk) else: if down['success']: - if options.verbose: + if options['verbose']: start_time = down['start_time'] headers_receipt = \ down['headers_receipt'] - start_time @@ -423,7 +421,7 @@ def st_download(parser, args, output_manager): obj = down['object'] if isinstance(error, ClientException): if error.http_status == 304 and \ - options.skip_identical: + options['skip_identical']: output_manager.print_msg( "Skipped identical file '%s'", path) continue @@ -467,17 +465,17 @@ Optional arguments: def st_list(parser, args, output_manager): - def _print_stats(options, stats): + def _print_stats(options, stats, human): total_count = total_bytes = 0 container = stats.get("container", None) for item in stats["listing"]: item_name = item.get('name') - if not options.long and not options.human: + if not options['long'] and not human: output_manager.print_msg(item.get('name', item.get('subdir'))) else: if not container: # listing containers item_bytes = item.get('bytes') - byte_str = prt_bytes(item_bytes, options.human) + byte_str = prt_bytes(item_bytes, human) count = item.get('count') total_count += count try: @@ -486,7 +484,7 @@ def st_list(parser, args, output_manager): datestamp = strftime('%Y-%m-%d %H:%M:%S', utc) except TypeError: datestamp = '????-??-?? ??:??:??' - if not options.totals: + if not options['totals']: output_manager.print_msg( "%5s %s %s %s", count, byte_str, datestamp, item_name) @@ -495,29 +493,29 @@ def st_list(parser, args, output_manager): content_type = item.get('content_type') if subdir is None: item_bytes = item.get('bytes') - byte_str = prt_bytes(item_bytes, options.human) + byte_str = prt_bytes(item_bytes, human) date, xtime = item.get('last_modified').split('T') xtime = xtime.split('.')[0] else: item_bytes = 0 - byte_str = prt_bytes(item_bytes, options.human) + byte_str = prt_bytes(item_bytes, human) date = xtime = '' item_name = subdir - if not options.totals: + if not options['totals']: output_manager.print_msg( "%s %10s %8s %24s %s", byte_str, date, xtime, content_type, item_name) total_bytes += item_bytes # report totals - if options.long or options.human: + if options['long'] or human: if not container: output_manager.print_msg( "%5s %s", prt_bytes(total_count, True), - prt_bytes(total_bytes, options.human)) + prt_bytes(total_bytes, human)) else: output_manager.print_msg( - prt_bytes(total_bytes, options.human)) + prt_bytes(total_bytes, human)) parser.add_argument( '-l', '--long', dest='long', action='store_true', default=False, @@ -540,20 +538,19 @@ def st_list(parser, args, output_manager): 'what this means.') options, args = parse_args(parser, args) args = args[1:] - if options.delimiter and not args: + if options['delimiter'] and not args: exit('-d option only allowed for container listings') - _opts = vars(options).copy() - if _opts['human']: - _opts.pop('human') - _opts['long'] = True + human = options.pop('human') + if human: + options['long'] = True - if options.totals and not options.long and not options.human: + if options['totals'] and not options['long']: output_manager.error( "Listing totals only works with -l or --lh.") return - with SwiftService(options=_opts) as swift: + with SwiftService(options=options) as swift: try: if not args: stats_parts_gen = swift.list() @@ -570,7 +567,7 @@ def st_list(parser, args, output_manager): for stats in stats_parts_gen: if stats["success"]: - _print_stats(options, stats) + _print_stats(options, stats, human) else: raise stats["error"] @@ -602,9 +599,7 @@ def st_stat(parser, args, output_manager): options, args = parse_args(parser, args) args = args[1:] - _opts = vars(options) - - with SwiftService(options=_opts) as swift: + with SwiftService(options=options) as swift: try: if not args: stat_result = swift.stat() @@ -713,13 +708,11 @@ def st_post(parser, args, output_manager): '-H "Content-Length: 4000"') (options, args) = parse_args(parser, args) args = args[1:] - if (options.read_acl or options.write_acl or options.sync_to or - options.sync_key) and not args: + if (options['read_acl'] or options['write_acl'] or options['sync_to'] or + options['sync_key']) and not args: exit('-r, -w, -t, and -k options only allowed for containers') - _opts = vars(options) - - with SwiftService(options=_opts) as swift: + with SwiftService(options=options) as swift: try: if not args: result = swift.post() @@ -868,47 +861,46 @@ def st_upload(parser, args, output_manager): container = args[0] files = args[1:] - if options.object_name is not None: + if options['object_name'] is not None: if len(files) > 1: output_manager.error('object-name only be used with 1 file or dir') return else: orig_path = files[0] - if options.segment_size: + if options['segment_size']: try: # If segment size only has digits assume it is bytes - int(options.segment_size) + int(options['segment_size']) except ValueError: try: - size_mod = "BKMG".index(options.segment_size[-1].upper()) - multiplier = int(options.segment_size[:-1]) + size_mod = "BKMG".index(options['segment_size'][-1].upper()) + multiplier = int(options['segment_size'][:-1]) except ValueError: output_manager.error("Invalid segment size") return - options.segment_size = str((1024 ** size_mod) * multiplier) - if int(options.segment_size) <= 0: + options['segment_size'] = str((1024 ** size_mod) * multiplier) + if int(options['segment_size']) <= 0: output_manager.error("segment-size should be positive") return - if options.object_threads <= 0: + if options['object_threads'] <= 0: output_manager.error( 'ERROR: option --object-threads should be a positive integer.' '\n\nUsage: %s upload %s\n%s', BASENAME, st_upload_options, st_upload_help) return - if options.segment_threads <= 0: + if options['segment_threads'] <= 0: output_manager.error( 'ERROR: option --segment-threads should be a positive integer.' '\n\nUsage: %s upload %s\n%s', BASENAME, st_upload_options, st_upload_help) return - _opts = vars(options) - _opts['object_uu_threads'] = options.object_threads - with SwiftService(options=_opts) as swift: + options['object_uu_threads'] = options['object_threads'] + with SwiftService(options=options) as swift: try: objs = [] dir_markers = [] @@ -926,25 +918,25 @@ def st_upload(parser, args, output_manager): # Now that we've collected all the required files and dir markers # build the tuples for the call to upload - if options.object_name is not None: + if options['object_name'] is not None: objs = [ SwiftUploadObject( o, object_name=o.replace( - orig_path, options.object_name, 1 + orig_path, options['object_name'], 1 ) ) for o in objs ] dir_markers = [ SwiftUploadObject( None, object_name=d.replace( - orig_path, options.object_name, 1 + orig_path, options['object_name'], 1 ), options={'dir_marker': True} ) for d in dir_markers ] for r in swift.upload(container, objs + dir_markers): if r['success']: - if options.verbose: + if options['verbose']: if 'attempts' in r and r['attempts'] > 1: if 'object' in r: output_manager.print_msg( @@ -990,7 +982,7 @@ def st_upload(parser, args, output_manager): output_manager.error("%s" % error) too_large = (isinstance(error, ClientException) and error.http_status == 413) - if too_large and options.verbose > 0: + if too_large and options['verbose'] > 0: output_manager.error( "Consider using the --segment-size option " "to chunk the object") @@ -1028,8 +1020,7 @@ def st_capabilities(parser, args, output_manager): st_capabilities_options, st_capabilities_help) return - _opts = vars(options) - with SwiftService(options=_opts) as swift: + with SwiftService(options=options) as swift: try: if len(args) == 2: url = args[1] @@ -1067,23 +1058,23 @@ Display auth related authentication variables in shell friendly format. def st_auth(parser, args, thread_manager): (options, args) = parse_args(parser, args) - _opts = vars(options) - if options.verbose > 1: - if options.auth_version in ('1', '1.0'): - print('export ST_AUTH=%s' % sh_quote(options.auth)) - print('export ST_USER=%s' % sh_quote(options.user)) - print('export ST_KEY=%s' % sh_quote(options.key)) + if options['verbose'] > 1: + if options['auth_version'] in ('1', '1.0'): + print('export ST_AUTH=%s' % sh_quote(options['auth'])) + print('export ST_USER=%s' % sh_quote(options['user'])) + print('export ST_KEY=%s' % sh_quote(options['key'])) else: print('export OS_IDENTITY_API_VERSION=%s' % sh_quote( - options.auth_version)) - print('export OS_AUTH_VERSION=%s' % sh_quote(options.auth_version)) - print('export OS_AUTH_URL=%s' % sh_quote(options.auth)) - for k, v in sorted(_opts.items()): + options['auth_version'])) + print('export OS_AUTH_VERSION=%s' % sh_quote( + options['auth_version'])) + print('export OS_AUTH_URL=%s' % sh_quote(options['auth'])) + for k, v in sorted(options.items()): if v and k.startswith('os_') and \ k not in ('os_auth_url', 'os_options'): print('export %s=%s' % (k.upper(), sh_quote(v))) else: - conn = get_conn(_opts) + conn = get_conn(options) url, token = conn.get_auth() print('export OS_STORAGE_URL=%s' % sh_quote(url)) print('export OS_AUTH_TOKEN=%s' % sh_quote(token)) @@ -1140,7 +1131,7 @@ def st_tempurl(parser, args, thread_manager): 'tempurl specified, possibly an error' % method.upper()) url = generate_temp_url(path, seconds, key, method, - absolute=options.absolute_expiry) + absolute=options['absolute_expiry']) thread_manager.print_msg(url) @@ -1179,16 +1170,17 @@ class HelpFormatter(argparse.HelpFormatter): def parse_args(parser, args, enforce_requires=True): options, args = parser.parse_known_args(args or ['-h']) - if enforce_requires and (options.debug or options.info): + options = vars(options) + if enforce_requires and (options['debug'] or options['info']): logging.getLogger("swiftclient") - if options.debug: + if options['debug']: logging.basicConfig(level=logging.DEBUG) logging.getLogger('iso8601').setLevel(logging.WARNING) client_logger_settings['redact_sensitive_headers'] = False - elif options.info: + elif options['info']: logging.basicConfig(level=logging.INFO) - if args and options.help: + if args and options['help']: _help = globals().get('st_%s_help' % args[0], "no help for %s" % args[0]) print(_help) @@ -1198,62 +1190,62 @@ def parse_args(parser, args, enforce_requires=True): if args and args[0] == 'tempurl': return options, args - if options.auth_version == '3.0': + if options['auth_version'] == '3.0': # tolerate sloppy auth_version - options.auth_version = '3' + options['auth_version'] = '3' - if (not (options.auth and options.user and options.key) - and options.auth_version != '3'): + if (not (options['auth'] and options['user'] and options['key']) + and options['auth_version'] != '3'): # Use keystone auth if any of the old-style args are missing - options.auth_version = '2.0' + options['auth_version'] = '2.0' # Use new-style args if old ones not present - if not options.auth and options.os_auth_url: - options.auth = options.os_auth_url - if not options.user and options.os_username: - options.user = options.os_username - if not options.key and options.os_password: - options.key = options.os_password + if not options['auth'] and options['os_auth_url']: + options['auth'] = options['os_auth_url'] + if not options['user'] and options['os_username']: + options['user'] = options['os_username'] + if not options['key'] and options['os_password']: + options['key'] = options['os_password'] # Specific OpenStack options - options.os_options = { - 'user_id': options.os_user_id, - 'user_domain_id': options.os_user_domain_id, - 'user_domain_name': options.os_user_domain_name, - 'tenant_id': options.os_tenant_id, - 'tenant_name': options.os_tenant_name, - 'project_id': options.os_project_id, - 'project_name': options.os_project_name, - 'project_domain_id': options.os_project_domain_id, - 'project_domain_name': options.os_project_domain_name, - 'service_type': options.os_service_type, - 'endpoint_type': options.os_endpoint_type, - 'auth_token': options.os_auth_token, - 'object_storage_url': options.os_storage_url, - 'region_name': options.os_region_name, + options['os_options'] = { + 'user_id': options['os_user_id'], + 'user_domain_id': options['os_user_domain_id'], + 'user_domain_name': options['os_user_domain_name'], + 'tenant_id': options['os_tenant_id'], + 'tenant_name': options['os_tenant_name'], + 'project_id': options['os_project_id'], + 'project_name': options['os_project_name'], + 'project_domain_id': options['os_project_domain_id'], + 'project_domain_name': options['os_project_domain_name'], + 'service_type': options['os_service_type'], + 'endpoint_type': options['os_endpoint_type'], + 'auth_token': options['os_auth_token'], + 'object_storage_url': options['os_storage_url'], + 'region_name': options['os_region_name'], } if len(args) > 1 and args[0] == "capabilities": return options, args - if (options.os_options.get('object_storage_url') and - options.os_options.get('auth_token') and - options.auth_version in ('2.0', '3')): + if (options['os_options']['object_storage_url'] and + options['os_options']['auth_token'] and + options['auth_version'] in ('2.0', '3')): return options, args if enforce_requires: - if options.auth_version == '3': - if not options.auth: + if options['auth_version'] == '3': + if not options['auth']: exit('Auth version 3 requires OS_AUTH_URL to be set or ' + 'overridden with --os-auth-url') - if not (options.user or options.os_user_id): + if not (options['user'] or options['os_user_id']): exit('Auth version 3 requires either OS_USERNAME or ' + 'OS_USER_ID to be set or overridden with ' + '--os-username or --os-user-id respectively.') - if not options.key: + if not options['key']: exit('Auth version 3 requires OS_PASSWORD to be set or ' + 'overridden with --os-password') - elif not (options.auth and options.user and options.key): + elif not (options['auth'] and options['user'] and options['key']): exit(''' Auth version 1.0 requires ST_AUTH, ST_USER, and ST_KEY environment variables to be set or overridden with -A, -U, or -K. @@ -1537,8 +1529,8 @@ Examples: 'Defaults to env[OS_CACERT].') options, args = parse_args(parser, argv[1:], enforce_requires=False) - if options.help or options.os_help: - if options.help: + if options['help'] or options['os_help']: + if options['help']: parser._action_groups.pop() parser.print_help() exit() diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py index 2a478369..0b299522 100644 --- a/tests/unit/test_shell.py +++ b/tests/unit/test_shell.py @@ -1438,18 +1438,18 @@ class TestParsing(TestBase): expected_os_opts_dict = expected_os_opts_dict or {} # check the expected opts are set for key, v in expected_opts.items(): - actual = getattr(actual_opts, key) + actual = actual_opts.get(key) self.assertEqual(v, actual, 'Expected %s for key %s, found %s' % (v, key, actual)) for key, v in expected_os_opts.items(): - actual = getattr(actual_opts, "os_" + key) + actual = actual_opts.get("os_" + key) self.assertEqual(v, actual, 'Expected %s for key %s, found %s' % (v, key, actual)) # check the os_options dict values are set - self.assertTrue(hasattr(actual_opts, 'os_options')) - actual_os_opts_dict = getattr(actual_opts, 'os_options') + self.assertIn('os_options', actual_opts) + actual_os_opts_dict = actual_opts['os_options'] expected_os_opts_keys = ['project_name', 'region_name', 'tenant_name', 'user_domain_name', 'endpoint_type', @@ -1478,8 +1478,8 @@ class TestParsing(TestBase): ('os_auth_url', 'auth'), ('os_password', 'key')] for pair in equivalents: - self.assertEqual(getattr(actual_opts, pair[0]), - getattr(actual_opts, pair[1])) + self.assertEqual(actual_opts.get(pair[0]), + actual_opts.get(pair[1])) def test_minimum_required_args_v3(self): opts = {"auth_version": "3"}