Commit 5852db11 authored by Stavros Sachtouris's avatar Stavros Sachtouris

Escape characters in CLI output methods

Closes grnet/kamaki#32

Kamaki CLI commands use some generic output methods to handle outputs.

Generic output methods that escape control characters:
- in kamaki.cli.utils: print_items, print_dict, print_list
- in kamaki.cli.cmds: error

Generic output methods that don't escape control characters:
- in kamaki.cli.cmds: writeln, write

The methods that don't escape control characters are used when the
standard methods are not sufficient. For example, in "kamaki.cli.pithos"
the "PithosAccount.print_objects" method adds decorative escape characters
(i.e., bold, alignment), but has to escape the parts object and container
names. To achieve this, escaping is handled in the method, and the output
is printed with the "write" and "writeln" methods mentioned earlier.

An encoding issue with kamaki.cli.errors.CLIError and the Exceptions extending it,
is fixed. It is now ensured that the error message will always be in unicode.

Also, fix flake8 errors on files affected by the above changes
parent 92573be9
...@@ -46,7 +46,7 @@ from kamaki.cli.errors import CLIError, CLICmdSpecError ...@@ -46,7 +46,7 @@ from kamaki.cli.errors import CLIError, CLICmdSpecError
from kamaki.cli import logger from kamaki.cli import logger
from kamaki.clients.astakos import CachedAstakosClient from kamaki.clients.astakos import CachedAstakosClient
from kamaki.clients import ClientError, KamakiSSLError from kamaki.clients import ClientError, KamakiSSLError
from kamaki.clients.utils import https from kamaki.clients.utils import https, escape_ctrl_chars
_debug = False _debug = False
...@@ -198,7 +198,8 @@ def _check_config_version(cnf): ...@@ -198,7 +198,8 @@ def _check_config_version(cnf):
print 'The following information will NOT be preserved:' print 'The following information will NOT be preserved:'
print '\t', '\n\t'.join(lost_terms) print '\t', '\n\t'.join(lost_terms)
print('Kamaki is ready to convert the config file') print('Kamaki is ready to convert the config file')
stdout.write('Create (overwrite) file %s ? [y/N] ' % cnf.path) stdout.write('Create (overwrite) file %s ? [y/N] ' % escape_ctrl_chars(
cnf.path))
from sys import stdin from sys import stdin
reply = stdin.readline() reply = stdin.readline()
if reply in ('Y\n', 'y\n'): if reply in ('Y\n', 'y\n'):
...@@ -443,7 +444,7 @@ def update_parser_help(parser, cmd): ...@@ -443,7 +444,7 @@ def update_parser_help(parser, cmd):
def print_error_message(cli_err, out=stderr): def print_error_message(cli_err, out=stderr):
errmsg = u'%s' % cli_err errmsg = escape_ctrl_chars(('%s' % cli_err).strip('\n'))
if cli_err.importance == 1: if cli_err.importance == 1:
errmsg = magenta(errmsg) errmsg = magenta(errmsg)
elif cli_err.importance == 2: elif cli_err.importance == 2:
...@@ -451,8 +452,9 @@ def print_error_message(cli_err, out=stderr): ...@@ -451,8 +452,9 @@ def print_error_message(cli_err, out=stderr):
elif cli_err.importance > 2: elif cli_err.importance > 2:
errmsg = red(errmsg) errmsg = red(errmsg)
out.write(errmsg) out.write(errmsg)
out.write('\n')
for errmsg in cli_err.details: for errmsg in cli_err.details:
out.write(u'| %s\n' % errmsg) out.write('| %s\n' % escape_ctrl_chars(u'%s' % errmsg))
out.flush() out.flush()
...@@ -570,7 +572,8 @@ def main(func): ...@@ -570,7 +572,8 @@ def main(func):
'global', 'ca_certs') 'global', 'ca_certs')
stderr.write(red('SSL Authentication failed\n')) stderr.write(red('SSL Authentication failed\n'))
if ca: if ca:
stderr.write('Path used for CA certifications file: %s\n' % ca) stderr.write('Path used for CA certifications file: %s\n' % (
escape_ctrl_chars(ca)))
stderr.write('Please make sure the path is correct\n') stderr.write('Please make sure the path is correct\n')
if not (ca_arg and ca_arg.value): if not (ca_arg and ca_arg.value):
stderr.write('| To set the correct path:\n') stderr.write('| To set the correct path:\n')
...@@ -586,7 +589,8 @@ def main(func): ...@@ -586,7 +589,8 @@ def main(func):
stderr.flush() stderr.flush()
if _debug: if _debug:
raise raise
stderr.write('| %s: %s\n' % (type(err), err)) stderr.write('| %s: %s\n' % (
type(err), escape_ctrl_chars('%s' % err)))
stderr.flush() stderr.flush()
exit(1) exit(1)
except KeyboardInterrupt: except KeyboardInterrupt:
......
...@@ -41,6 +41,7 @@ from kamaki.cli.utils import ( ...@@ -41,6 +41,7 @@ from kamaki.cli.utils import (
from kamaki.cli.argument import ValueArgument, ProgressBarArgument from kamaki.cli.argument import ValueArgument, ProgressBarArgument
from kamaki.cli.errors import CLIInvalidArgument, CLIBaseUrlError from kamaki.cli.errors import CLIInvalidArgument, CLIBaseUrlError
from kamaki.cli.cmds import errors from kamaki.cli.cmds import errors
from kamaki.clients.utils import escape_ctrl_chars
log = get_logger(__name__) log = get_logger(__name__)
...@@ -144,7 +145,8 @@ class CommandInit(object): ...@@ -144,7 +145,8 @@ class CommandInit(object):
self.write('%s\n' % s) self.write('%s\n' % s)
def error(self, s=''): def error(self, s=''):
self._err.write(('%s\n' % s).encode(pref_enc, errors='replace')) esc_s = escape_ctrl_chars(s)
self._err.write(('%s\n' % esc_s).encode(pref_enc, errors='replace'))
self._err.flush() self._err.flush()
def print_list(self, *args, **kwargs): def print_list(self, *args, **kwargs):
...@@ -204,19 +206,19 @@ class CommandInit(object): ...@@ -204,19 +206,19 @@ class CommandInit(object):
self['config'].get('global', 'log_token').lower() == 'on') self['config'].get('global', 'log_token').lower() == 'on')
except Exception as e: except Exception as e:
log.debug('Failed to read custom log_token setting:' log.debug('Failed to read custom log_token setting:'
'%s\n default for log_token is off' % e) '%s\n default for log_token is off' % e)
try: try:
self.client.LOG_DATA = ( self.client.LOG_DATA = (
self['config'].get('global', 'log_data').lower() == 'on') self['config'].get('global', 'log_data').lower() == 'on')
except Exception as e: except Exception as e:
log.debug('Failed to read custom log_data setting:' log.debug('Failed to read custom log_data setting:'
'%s\n default for log_data is off' % e) '%s\n default for log_data is off' % e)
try: try:
self.client.LOG_PID = ( self.client.LOG_PID = (
self['config'].get('global', 'log_pid').lower() == 'on') self['config'].get('global', 'log_pid').lower() == 'on')
except Exception as e: except Exception as e:
log.debug('Failed to read custom log_pid setting:' log.debug('Failed to read custom log_pid setting:'
'%s\n default for log_pid is off' % e) '%s\n default for log_pid is off' % e)
def _safe_progress_bar( def _safe_progress_bar(
self, msg, arg='progress_bar', countdown=False, timeout=100): self, msg, arg='progress_bar', countdown=False, timeout=100):
......
...@@ -344,10 +344,10 @@ class user_select(_AstakosInit): ...@@ -344,10 +344,10 @@ class user_select(_AstakosInit):
name = self.astakos.user_info()['name'] or '<USER>' name = self.astakos.user_info()['name'] or '<USER>'
if self.astakos.token != first_token: if self.astakos.token != first_token:
self.astakos.token = first_token self.astakos.token = first_token
msg = 'User %s with id %s is now the current session user\n' % ( self.error('User %s with id %s is now the current session user' % (
name, uuid) name, uuid))
msg += 'Make %s the default user for future sessions?' % name if self.ask_user(
if self.ask_user(msg): 'Make %s the default user for future sessions?' % name):
tokens = self.astakos._uuids.keys() tokens = self.astakos._uuids.keys()
tokens.remove(self.astakos.token) tokens.remove(self.astakos.token)
tokens.insert(0, self.astakos.token) tokens.insert(0, self.astakos.token)
...@@ -390,7 +390,8 @@ class user_delete(_AstakosInit): ...@@ -390,7 +390,8 @@ class user_delete(_AstakosInit):
try: try:
self.astakos.remove_user(uuid) self.astakos.remove_user(uuid)
except KeyError: except KeyError:
raise CLIError('No user with uuid %s in session list' % uuid, raise CLIError(
'No user with uuid %s in session list' % uuid,
details=[ details=[
'To see all cached session users', 'To see all cached session users',
' kamaki user list', ' kamaki user list',
...@@ -734,8 +735,8 @@ class PolicyArgument(ValueArgument): ...@@ -734,8 +735,8 @@ class PolicyArgument(ValueArgument):
self._value = new_policy.lower() self._value = new_policy.lower()
else: else:
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid value for %s' % self.lvalue, details=[ 'Invalid value for %s' % self.lvalue,
'Valid values: %s' % ', '.join(self.policies)]) details=['Valid values: %s' % ', '.join(self.policies)])
class ProjectResourceArgument(KeyValueArgument): class ProjectResourceArgument(KeyValueArgument):
...@@ -761,12 +762,12 @@ class ProjectResourceArgument(KeyValueArgument): ...@@ -761,12 +762,12 @@ class ProjectResourceArgument(KeyValueArgument):
except Exception as e: except Exception as e:
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid resource value %s' % value, details=[ 'Invalid resource value %s' % value, details=[
'Usage:', 'Usage:',
' %s %s=<member_capacity>,<project_capacity>' % ( ' %s %s=<member_capacity>,<project_capacity>' % (
self.lvalue, key), self.lvalue, key),
'where both capacities are integers', 'where both capacities are integers',
'and member_capacity <= project_capacity', '', 'and member_capacity <= project_capacity', '',
'(%s)' % e]) '(%s)' % e])
self._value[key] = dict( self._value[key] = dict(
member_capacity=member_capacity, member_capacity=member_capacity,
project_capacity=project_capacity) project_capacity=project_capacity)
...@@ -911,8 +912,8 @@ class project_modify(_AstakosInit, OptionalOutput): ...@@ -911,8 +912,8 @@ class project_modify(_AstakosInit, OptionalOutput):
a = self.arguments a = self.arguments
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid argument combination', details=[ 'Invalid argument combination', details=[
'Arguments %s and %s are mutually exclussive' % ( 'Arguments %s and %s are mutually exclussive' % (
a['private'].lvalue, a['public'].lvalue)]) a['private'].lvalue, a['public'].lvalue)])
self._run(project_id=project_id) self._run(project_id=project_id)
......
...@@ -153,7 +153,7 @@ class server_list(_CycladesInit, OptionalOutput, NameFilter, IDFilter): ...@@ -153,7 +153,7 @@ class server_list(_CycladesInit, OptionalOutput, NameFilter, IDFilter):
def _add_user_name(self, servers): def _add_user_name(self, servers):
uuids = self._uuids2usernames(list(set( uuids = self._uuids2usernames(list(set(
[srv['user_id'] for srv in servers]))) [srv['user_id'] for srv in servers])))
for srv in servers: for srv in servers:
srv['user_id'] += ' (%s)' % uuids[srv['user_id']] srv['user_id'] += ' (%s)' % uuids[srv['user_id']]
return servers return servers
...@@ -179,7 +179,7 @@ class server_list(_CycladesInit, OptionalOutput, NameFilter, IDFilter): ...@@ -179,7 +179,7 @@ class server_list(_CycladesInit, OptionalOutput, NameFilter, IDFilter):
def _filter_by_metadata(self, servers): def _filter_by_metadata(self, servers):
new_servers = [] new_servers = []
for srv in servers: for srv in servers:
if not 'metadata' in srv: if 'metadata' not in srv:
continue continue
meta = [dict(srv['metadata'])] meta = [dict(srv['metadata'])]
if self['meta']: if self['meta']:
...@@ -370,8 +370,8 @@ class NetworkArgument(RepeatableArgument): ...@@ -370,8 +370,8 @@ class NetworkArgument(RepeatableArgument):
if (part2.startswith('id=') and netid) or ( if (part2.startswith('id=') and netid) or (
part2.startswith('ip=') and ip): part2.startswith('ip=') and ip):
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid network argument %s' % v, details=[ 'Invalid network argument %s' % v,
'Valid format: [id=]NETWORK_ID[,[ip=]IP]']) details=['Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
if part2.startswith('id='): if part2.startswith('id='):
netid = part2[len('id='):] netid = part2[len('id='):]
elif part2.startswith('ip='): elif part2.startswith('ip='):
...@@ -382,8 +382,8 @@ class NetworkArgument(RepeatableArgument): ...@@ -382,8 +382,8 @@ class NetworkArgument(RepeatableArgument):
netid = part2 netid = part2
if not netid: if not netid:
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid network argument %s' % v, details=[ 'Invalid network argument %s' % v,
'Valid format: [id=]NETWORK_ID[,[ip=]IP]']) details=['Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
self._value = getattr(self, '_value', []) self._value = getattr(self, '_value', [])
self._value.append(dict(uuid=netid)) self._value.append(dict(uuid=netid))
if ip: if ip:
...@@ -459,7 +459,7 @@ class server_create(_CycladesInit, OptionalOutput, _ServerWait): ...@@ -459,7 +459,7 @@ class server_create(_CycladesInit, OptionalOutput, _ServerWait):
self.error('Failed to build %s servers' % size) self.error('Failed to build %s servers' % size)
self.error('Found %s matching servers:' % len(spawned_servers)) self.error('Found %s matching servers:' % len(spawned_servers))
self.print_(spawned_servers, out=self._err) self.print_(spawned_servers, out=self._err)
self.error('Check if any of these servers should be removed\n') self.error('Check if any of these servers should be removed')
except Exception as ne: except Exception as ne:
self.error('Error (%s) while notifying about errors' % ne) self.error('Error (%s) while notifying about errors' % ne)
finally: finally:
...@@ -537,9 +537,9 @@ class server_create(_CycladesInit, OptionalOutput, _ServerWait): ...@@ -537,9 +537,9 @@ class server_create(_CycladesInit, OptionalOutput, _ServerWait):
if self['no_network'] and self['network_configuration']: if self['no_network'] and self['network_configuration']:
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid argument compination', importance=2, details=[ 'Invalid argument compination', importance=2, details=[
'Arguments %s and %s are mutually exclusive' % ( 'Arguments %s and %s are mutually exclusive' % (
self.arguments['no_network'].lvalue, self.arguments['no_network'].lvalue,
self.arguments['network_configuration'].lvalue)]) self.arguments['network_configuration'].lvalue)])
self._run() self._run()
...@@ -736,7 +736,7 @@ class server_delete(_CycladesInit, _ServerWait): ...@@ -736,7 +736,7 @@ class server_delete(_CycladesInit, _ServerWait):
deleted_vms.append(server_id) deleted_vms.append(server_id)
if self['cluster']: if self['cluster']:
dlen = len(deleted_vms) dlen = len(deleted_vms)
self.error('%s virtual server%s deleted' % ( self.error('%s virtual server %s deleted' % (
dlen, '' if dlen == 1 else 's')) dlen, '' if dlen == 1 else 's'))
def main(self, server_id_or_cluster_prefix): def main(self, server_id_or_cluster_prefix):
......
...@@ -58,7 +58,7 @@ class Generic(object): ...@@ -58,7 +58,7 @@ class Generic(object):
raise e raise e
elif isinstance(e, ClientError): elif isinstance(e, ClientError):
raise CLIError( raise CLIError(
u'(%s) %s' % (getattr(e, 'status', 'no status'), e), '(%s) %s' % (getattr(e, 'status', 'no status'), e),
details=getattr(e, 'details', []), details=getattr(e, 'details', []),
importance=1 if ( importance=1 if (
e.status < 200) else 2 if ( e.status < 200) else 2 if (
...@@ -250,8 +250,8 @@ class Cyclades(object): ...@@ -250,8 +250,8 @@ class Cyclades(object):
'Cluster size must be a positive integer', '%s' % ve]) 'Cluster size must be a positive integer', '%s' % ve])
except AssertionError as ae: except AssertionError as ae:
raise CLIError( raise CLIError(
'Invalid cluster size %s' % size, importance=1, details=[ 'Invalid cluster size %s' % size,
'%s' % ae]) importance=1, details=['%s' % ae])
except ClientError: except ClientError:
raise raise
_raise.__name__ = func.__name__ _raise.__name__ = func.__name__
...@@ -276,7 +276,7 @@ class Cyclades(object): ...@@ -276,7 +276,7 @@ class Cyclades(object):
'No network with id %s found' % network_id, 'No network with id %s found' % network_id,
importance=2, importance=2,
details=[msg, ] + this.about_network_id + [ details=[msg, ] + this.about_network_id + [
'%s %s' % (getattr(ce, 'status', ''), ce)]) '%s %s' % (getattr(ce, 'status', ''), ce)])
raise raise
_raise.__name__ = func.__name__ _raise.__name__ = func.__name__
return _raise return _raise
...@@ -438,7 +438,7 @@ class Cyclades(object): ...@@ -438,7 +438,7 @@ class Cyclades(object):
if ce.status in (404, 400): if ce.status in (404, 400):
server_id = kwargs.get('server_id', None) server_id = kwargs.get('server_id', None)
details = [ details = [
'to get a list of all servers', ' kamaki server list'] 'to get a list of all servers', ' kamaki server list']
if ce.status in (404, ): if ce.status in (404, ):
try: try:
server_id = int(server_id) server_id = int(server_id)
...@@ -447,7 +447,7 @@ class Cyclades(object): ...@@ -447,7 +447,7 @@ class Cyclades(object):
raise CLIError( raise CLIError(
'No servers with ID %s' % server_id, 'No servers with ID %s' % server_id,
importance=2, details=details + [ importance=2, details=details + [
'%s %s' % (getattr(ce, 'status', ''), ce)]) '%s %s' % (getattr(ce, 'status', ''), ce)])
raise raise
_raise.__name__ = func.__name__ _raise.__name__ = func.__name__
return _raise return _raise
...@@ -588,12 +588,13 @@ class Pithos(object): ...@@ -588,12 +588,13 @@ class Pithos(object):
except IOError as ioe: except IOError as ioe:
raise CLIError( raise CLIError(
'Failed to access a local file', importance=2, details=[ 'Failed to access a local file', importance=2, details=[
'To check if the file exists', ' kamaki file info PATH', 'To check if the file exists',
'All directories in a remote path must exist, or the ' ' kamaki file info PATH',
'download will fail', 'All directories in a remote path must exist, or the '
'To create a remote directory', 'download will fail',
' kamaki file mkdir REMOTE_DIRECTORY_PATH', 'To create a remote directory',
'%s' % ioe]) ' kamaki file mkdir REMOTE_DIRECTORY_PATH',
u'%s' % ioe])
_raise.__name__ = func.__name__ _raise.__name__ = func.__name__
return _raise return _raise
...@@ -606,7 +607,7 @@ class Pithos(object): ...@@ -606,7 +607,7 @@ class Pithos(object):
except IOError as ioe: except IOError as ioe:
raise CLIError( raise CLIError(
'Failed to access file %s' % local_path, 'Failed to access file %s' % local_path,
details=['%s' % ioe, ], importance=2) details=[u'%s' % ioe, ], importance=2)
_raise.__name__ = func.__name__ _raise.__name__ = func.__name__
return _raise return _raise
......
...@@ -68,7 +68,7 @@ class history_show(_HistoryInit): ...@@ -68,7 +68,7 @@ class history_show(_HistoryInit):
@errors.Generic.all @errors.Generic.all
def _run(self, cmd_slice): def _run(self, cmd_slice):
c = self.history.counter c = self.history.counter
lines = ['%s.\t%s' % (i + c, l) for i, l in enumerate( lines = ['%s. %s' % (i + c, l) for i, l in enumerate(
self.history[:])][cmd_slice] self.history[:])][cmd_slice]
if not isinstance(cmd_slice, slice): if not isinstance(cmd_slice, slice):
lines = [lines, ] lines = [lines, ]
......
...@@ -470,10 +470,10 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -470,10 +470,10 @@ class image_register(_ImageInit, OptionalOutput):
try: try:
pithos.get_object_info(path) pithos.get_object_info(path)
raise CLIError('File already exists', importance=2, details=[ raise CLIError('File already exists', importance=2, details=[
'A remote file /%s/%s already exists' % ( 'A remote file /%s/%s already exists' % (
pithos.container, path), pithos.container, path),
'Use %s to force upload' % self.arguments[ 'Use %s to force upload' % self.arguments[
'force_upload'].lvalue]) 'force_upload'].lvalue])
except ClientError as ce: except ClientError as ce:
if ce.status != 404: if ce.status != 404:
raise raise
...@@ -502,18 +502,18 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -502,18 +502,18 @@ class image_register(_ImageInit, OptionalOutput):
self._load_params_from_args(params, properties) self._load_params_from_args(params, properties)
if not self['no_metafile_upload']: if not self['no_metafile_upload']:
#check if metafile exists # check if metafile exists
pithos = pithos or self._get_pithos_client(locator) pithos = pithos or self._get_pithos_client(locator)
meta_path = '%s.meta' % locator.path meta_path = '%s.meta' % locator.path
self._assert_remote_file_not_exist(pithos, meta_path) self._assert_remote_file_not_exist(pithos, meta_path)
#register the image # register the image
try: try:
r = self.client.register(name, location, params, properties) r = self.client.register(name, location, params, properties)
except ClientError as ce: except ClientError as ce:
if ce.status in (400, 404): if ce.status in (400, 404):
raise CLIError( raise CLIError(
'Nonexistent image file location\n\t%s' % location, 'Nonexistent image file location %s' % location,
details=[ details=[
'%s' % ce, '%s' % ce,
'Does the image file %s exist at container %s ?' % ( 'Does the image file %s exist at container %s ?' % (
...@@ -523,7 +523,7 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -523,7 +523,7 @@ class image_register(_ImageInit, OptionalOutput):
r['owner'] += ' (%s)' % self._uuid2username(r['owner']) r['owner'] += ' (%s)' % self._uuid2username(r['owner'])
self.print_(r, self.print_dict) self.print_(r, self.print_dict)
#upload the metadata file # upload the metadata file
if not self['no_metafile_upload']: if not self['no_metafile_upload']:
try: try:
meta_headers = pithos.upload_from_string( meta_headers = pithos.upload_from_string(
...@@ -531,9 +531,8 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -531,9 +531,8 @@ class image_register(_ImageInit, OptionalOutput):
sharing=dict(read='*' if params.get('is_public') else ''), sharing=dict(read='*' if params.get('is_public') else ''),
container_info_cache=self.container_info_cache) container_info_cache=self.container_info_cache)
except TypeError: except TypeError:
self.error( self.error('Failed to dump metafile /%s/%s' % (
'Failed to dump metafile /%s/%s' % ( locator.container, meta_path))
locator.container, meta_path))
return return
if self['output_format']: if self['output_format']:
self.print_json(dict( self.print_json(dict(
......
...@@ -38,6 +38,7 @@ from os import path, walk, makedirs ...@@ -38,6 +38,7 @@ from os import path, walk, makedirs
from threading import activeCount, enumerate as activethreads from threading import activeCount, enumerate as activethreads
from kamaki.clients.pithos import PithosClient, ClientError from kamaki.clients.pithos import PithosClient, ClientError
from kamaki.clients.utils import escape_ctrl_chars
from kamaki.cli import command from kamaki.cli import command
from kamaki.cli.cmdtree import CommandTree from kamaki.cli.cmdtree import CommandTree
...@@ -120,7 +121,8 @@ class _PithosAccount(_PithosInit): ...@@ -120,7 +121,8 @@ class _PithosAccount(_PithosInit):
else: else:
size = format_size(obj['bytes']) size = format_size(obj['bytes'])
pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size) pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size)
oname = obj['name'] if self['more'] else bold(obj['name']) oname = escape_ctrl_chars(obj['name'])
oname = oname if self['more'] else bold(oname)
prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else '' prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else ''
if self['detail']: if self['detail']:
self.writeln('%s%s' % (prfx, oname)) self.writeln('%s%s' % (prfx, oname))
...@@ -577,17 +579,16 @@ class _PithosFromTo(_PithosContainer): ...@@ -577,17 +579,16 @@ class _PithosFromTo(_PithosContainer):
self.error(' delete source directory %s' % src) self.error(' delete source directory %s' % src)
return return
dst_prf = '' if self.account == self.dst_client.account else ( dst_prf = '' if self.account == self.dst_client.account else (
'pithos://%s' % self.dst_client.account) 'pithos://%s' % self.dst_client.account)
full_dest_path = '%s/%s/%s' % (dst_prf, self.dst_client.container, dst)
if src: if src:
src_prf = '' if self.account == self.dst_client.account else ( src_prf = '' if self.account == self.dst_client.account else (
'pithos://%s' % self.account) 'pithos://%s' % self.account)
self.error(' %s %s/%s/%s\n --> %s/%s/%s' % ( full_src_path = '/%s/%s/%s' % (src_prf, self.container, src)
transfer_name, self.error(' %s %s --> %s' % (
src_prf, self.container, src, transfer_name, full_src_path, full_dest_path))
dst_prf, self.dst_client.container, dst))
else: else:
self.error(' mkdir %s/%s/%s' % ( self.error(' mkdir %s' % full_dest_path)
dst_prf, self.dst_client.container, dst))
@errors.Generic.all @errors.Generic.all
@errors.Pithos.account @errors.Pithos.account
...@@ -633,15 +634,13 @@ class _PithosFromTo(_PithosContainer): ...@@ -633,15 +634,13 @@ class _PithosFromTo(_PithosContainer):
'Destination object exists', importance=2, details=[ 'Destination object exists', importance=2, details=[
'Failed while transfering:', 'Failed while transfering:',
' pithos://%s/%s/%s' % ( ' pithos://%s/%s/%s' % (
self.account, self.account, self.container, src_path),
self.container,
src_path),
'--> pithos://%s/%s/%s' % ( '--> pithos://%s/%s/%s' % (
self.dst_client.account, self.dst_client.account,
self.dst_client.container, self.dst_client.container,
dst_path), dst_path),
'Use %s to transfer overwrite' % ( 'Use %s to transfer overwrite' % (
self.arguments['force'].lvalue)]) self.arguments['force'].lvalue)])
else: else:
# One object transfer # One object transfer
try: try:
...@@ -684,15 +683,13 @@ class _PithosFromTo(_PithosContainer): ...@@ -684,15 +683,13 @@ class _PithosFromTo(_PithosContainer):
importance=2, details=[ importance=2, details=[
'Failed while transfering:', 'Failed while transfering:',
' pithos://%s/%s/%s' % ( ' pithos://%s/%s/%s' % (
self.account, self.account, self.container, self.path),
self.container,
self.path),
'--> pithos://%s/%s/%s' % ( '--> pithos://%s/%s/%s' % (
self.dst_client.account, self.dst_client.account,
self.dst_client.container, self.dst_client.container,
dst_path), dst_path),
'Use %s to transfer overwrite' % ( 'Use %s to transfer overwrite' % (
self.arguments['force'].lvalue)]) self.arguments['force'].lvalue)])
return pairs return pairs
def _run(self, source_path_or_url, destination_path_or_url=''): def _run(self, source_path_or_url, destination_path_or_url=''):
...@@ -1011,7 +1008,7 @@ class file_upload(_PithosContainer): ...@@ -1011,7 +1008,7 @@ class file_upload(_PithosContainer):
pathfix = f.replace(path.sep, '/') pathfix = f.replace(path.sep, '/')
yield open(fpath, 'rb'), '%s/%s' % (rel_path, pathfix) yield open(fpath, 'rb'), '%s/%s' % (rel_path, pathfix)
else: else:
self.error('%s is not a regular file' % fpath) self.error('%s not a regular file' % fpath)
else: else:
if not path.isfile(lpath): if not path.isfile(lpath):
raise CLIError(('%s is not a regular file' % lpath) if ( raise CLIError(('%s is not a regular file' % lpath) if (
...@@ -1131,11 +1128,11 @@ class RangeArgument(ValueArgument): ...@@ -1131,11 +1128,11 @@ class RangeArgument(ValueArgument):
if start > end: if start > end:
raise CLIInvalidArgument( raise CLIInvalidArgument(
'Invalid range %s' % newvalue, details=[ 'Invalid range %s' % newvalue, details=[
'Valid range formats', 'Valid range formats',
' START-END', ' UP_TO', ' -FROM', ' START-END', ' UP_TO', ' -FROM',
'where all values are integers', 'where all values are integers',
'OR a compination (csv), e.g.,', 'OR a compination (csv), e.g.,',
' %s=5,10-20,-5' % self.lvalue]) ' %s=5,10-20,-5' % self.lvalue])
self._value += '%s-%s' % (start, end) self._value += '%s-%s' % (start, end)
else: else:
self._value += '-%s' % int(end) self._value += '-%s' % int(end)
...@@ -1538,8 +1535,8 @@ class container_list(_PithosAccount, OptionalOutput, NameFilter): ...@@ -1538,8 +1535,8 @@ class container_list(_PithosAccount, OptionalOutput, NameFilter):
if 'bytes' in container: if 'bytes' in container:
size = format_size(container['bytes']) size = format_size(container['bytes'])
prfx = ('%s. ' % (index + 1)) if self['enum'] else '' prfx = ('%s. ' % (index + 1)) if self['enum'] else ''
_cname = container['name'] if ( _cname = escape_ctrl_chars(container['name'])
self['more']) else bold(container['name']) _cname = _cname if self['more'] else bold(_cname)
cname = u'%s%s' % (prfx, _cname) cname = u'%s%s' % (prfx, _cname)
if self['detail']: if self['detail']:
self.writeln(cname) self.writeln(cname)
...@@ -1650,7 +1647,7 @@ class container_create(_PithosAccount): ...@@ -1650,7 +1647,7 @@ class container_create(_PithosAccount):
if ce.status in (202, ): if ce.status in (202, ):
raise CLIError( raise CLIError(
'Container %s alread exists' % self.container, details=[ 'Container %s alread exists' % self.container, details=[