Skip to content

Commit

Permalink
Modify module methods in kamaki.cli classes
Browse files Browse the repository at this point in the history
Refs: grnet#9

Renamed to comply with pep8:
kamaki.cli.config.Config: _cloud_name --> cloud_name
kamaki.cli.shell: _init_shell --> init_shell

Merged with caller methods because they where used only once:
kamaki.cli: _construct_command_syntax, _num_of_matching_terms
kamaki.cli.utils: _parse_with_regex
kamaki.cli.one_cmd: _get_cmd_tree_from_spec, _get_best_match_from_cmd_tree
  • Loading branch information
saxtouri committed Apr 3, 2014
1 parent b7e866d commit 7a42aa0
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 110 deletions.
49 changes: 25 additions & 24 deletions kamaki/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,6 @@ def _arg2syntax(arg):
'_', ' ')


def _construct_command_syntax(cls):
spec = getargspec(cls.main.im_func)
args = spec.args[1:]
n = len(args) - len(spec.defaults or ())
required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
cls.syntax = ' '.join([required, optional])
if spec.varargs:
cls.syntax += ' <%s ...>' % spec.varargs


def _num_of_matching_terms(basic_list, attack_list):
if not attack_list:
return len(basic_list)
Expand All @@ -91,20 +80,24 @@ def _num_of_matching_terms(basic_list, attack_list):


def _update_best_match(name_terms, prefix=[]):
global _best_match
if prefix:
pref_list = prefix if isinstance(prefix, list) else prefix.split('_')
num_of_matching_terms = 0
for i, term in enumerate(name_terms):
try:
if term == pref_list[i]:
num_of_matching_terms += 1
else:
break
except IndexError:
break
if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
if len(_best_match) < num_of_matching_terms:
_best_match = name_terms[:num_of_matching_terms]
return True
else:
pref_list = []

num_of_matching_terms = _num_of_matching_terms(name_terms, pref_list)
global _best_match
if not prefix:
_best_match = []

if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
if len(_best_match) < num_of_matching_terms:
_best_match = name_terms[:num_of_matching_terms]
return True
return False


Expand Down Expand Up @@ -152,7 +145,15 @@ def wrap(cls):
except AttributeError:
raise CLICmdSpecError(
'No commend in %s (acts as cmd description)' % cls.__name__)
_construct_command_syntax(cls)
# Build command syntax help
spec = getargspec(cls.main.im_func)
args = spec.args[1:]
n = len(args) - len(spec.defaults or ())
required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
cls.syntax = ' '.join([required, optional])
if spec.varargs:
cls.syntax += ' <%s ...>' % spec.varargs

cmd_tree.add_command(
cls_name, cls.description, cls, cls.long_description)
Expand Down Expand Up @@ -540,7 +541,6 @@ def wrap():
def run_shell(exe, parser):
parser.arguments['help'].value = False
cloud = _init_session(parser.arguments)
from shell import _init_shell
global kloger
_cnf = parser.arguments['config']
auth_base = init_cached_authenticator(_cnf, cloud, kloger)
Expand All @@ -549,7 +549,8 @@ def run_shell(exe, parser):
auth_base.user_term('name'), auth_base.user_term('id'))
except Exception:
username, userid = '', ''
shell = _init_shell(exe, parser, username, userid)
from kamaki.cli.shell import init_shell
shell = init_shell(exe, parser, username, userid)
_load_all_commands(shell.cmd_tree, parser.arguments)
shell.run(auth_base, cloud, parser)

Expand Down
6 changes: 3 additions & 3 deletions kamaki/cli/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ def __init__(self, path=None, with_defaults=True):
self.read(self.path)

for section in self.sections():
r = self._cloud_name(section)
r = self.cloud_name(section)
if r:
for k, v in self.items(section):
self.set_cloud(r, k, v)
self.remove_section(section)

@staticmethod
def _cloud_name(full_section_name):
def cloud_name(full_section_name):
if not full_section_name.startswith(CLOUD_PREFIX + ' '):
return None
matcher = match(CLOUD_PREFIX + ' "([~@#$.:\-\w]+)"', full_section_name)
Expand Down Expand Up @@ -369,7 +369,7 @@ def set(self, section, option, value):
"""
prefix = CLOUD_PREFIX + '.'
if section.startswith(prefix):
cloud = self._cloud_name(
cloud = self.cloud_name(
CLOUD_PREFIX + ' "' + section[len(prefix):] + '"')
return self.set_cloud(cloud, option, value)
if section not in RawConfigParser.sections(self):
Expand Down
16 changes: 8 additions & 8 deletions kamaki/cli/config/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ def test___init__(
c_remove_section_num, gen_call = 0, [call('a'), call('b')]
for path, with_defaults in product((None, '/a/path'), (True, False)):
with patch(
'kamaki.cli.config.Config._cloud_name',
return_value=_2value_gen.next()) as _cloud_name:
'kamaki.cli.config.Config.cloud_name',
return_value=_2value_gen.next()) as cloud_name:
cnf = Config(path=path, with_defaults=with_defaults)
self.assertTrue(isinstance(cnf, RawConfigParser))
cpath = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
Expand All @@ -137,7 +137,7 @@ def test___init__(
self.assertEqual(len(c_sections.mock_calls), c_sections_num)
self.assertEqual(c_sections.mock_calls[-1], call())

self.assertEqual(_cloud_name.mock_calls, gen_call)
self.assertEqual(cloud_name.mock_calls, gen_call)

r = _2value_gen.next()
if r:
Expand All @@ -154,10 +154,10 @@ def test___init__(
self.assertEqual(
len(c_remove_section.mock_calls), c_remove_section_num)

def test__cloud_name(self):
def test_cloud_name(self):
from kamaki.cli.config import (
Config, CLOUD_PREFIX, InvalidCloudNameError)
cn = Config._cloud_name
cn = Config.cloud_name
self.assertEqual(cn('non%s name' % CLOUD_PREFIX), None)
for invalid in ('"!@#$%^&())_"', '"a b c"', u'"\xce\xcd"', 'naked'):
self.assertRaises(
Expand Down Expand Up @@ -385,15 +385,15 @@ def test_set(self):
_cnf = Config(path=self.f.name)

with patch(
'kamaki.cli.config.Config._cloud_name',
return_value='cn') as _cloud_name:
'kamaki.cli.config.Config.cloud_name',
return_value='cn') as cloud_name:
with patch(
'kamaki.cli.config.Config.set_cloud',
return_value='sc') as set_cloud:
self.assertEqual(
'sc', _cnf.set('%s.sec' % CLOUD_PREFIX, 'opt', 'val'))
self.assertEqual(
_cloud_name.mock_calls[-1],
cloud_name.mock_calls[-1],
call('%s "sec"' % CLOUD_PREFIX))
self.assertEqual(
set_cloud.mock_calls[-1], call('cn', 'opt', 'val'))
Expand Down
34 changes: 14 additions & 20 deletions kamaki/cli/one_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,6 @@
from kamaki.cli.errors import CLIUnknownCommand


def _get_cmd_tree_from_spec(spec, cmd_tree_list):
for tree in cmd_tree_list:
if tree.name == spec:
return tree
raise CLIUnknownCommand('Unknown command: %s' % spec)


def _get_best_match_from_cmd_tree(cmd_tree, unparsed):
matched = [term for term in unparsed if not term.startswith('-')]
while matched:
try:
return cmd_tree.get_command('_'.join(matched))
except KeyError:
matched = matched[:-1]
return None


def run(cloud, parser):
group = get_command_group(list(parser.unparsed), parser.arguments)
if not group:
Expand All @@ -79,13 +62,24 @@ def run(cloud, parser):
'Make sure %s is a valid command group' % group,
'Refer to kamaki documentation for setting custom command',
'groups or overide existing ones'])
cmd_tree = _get_cmd_tree_from_spec(group, spec_module.namespaces)
# Get command tree from group
try:
cmd_tree = [t for t in spec_module.namespaces if t.name == group][0]
except IndexError:
raise CLIUnknownCommand('Unknown command group: %s' % group)

cmd = None
if _best_match:
cmd = cmd_tree.get_command('_'.join(_best_match))
else:
cmd = _get_best_match_from_cmd_tree(cmd_tree, parser.unparsed)
_best_match = cmd.path.split('_')
match = [term for term in parser.unparsed if not term.startswith('-')]
while match:
try:
cmd = cmd_tree.get_command('_'.join(match))
_best_match = cmd.path.split('_')
break
except KeyError:
match = match[:-1]
if cmd is None:
kloger.info('Unexpected error: failed to load command (-d for more)')
exit(1)
Expand Down
2 changes: 1 addition & 1 deletion kamaki/cli/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
log = add_file_logger(__name__)


def _init_shell(exe_string, parser, username='', userid=''):
def init_shell(exe_string, parser, username='', userid=''):
parser.arguments.pop('version', None)
shell = Shell()
shell.set_prompt(exe_string)
Expand Down
71 changes: 17 additions & 54 deletions kamaki/cli/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,6 @@ def remove_colors():
red = yellow = magenta = bold = lambda x: x


def _encode_nicely(somestr, encoding, replacement='?'):
"""Encode somestr as 'encoding', but don't raise errors (replace with ?)
This method is slow. Us it only for grace.
:param encoding: (str) encode every character in this encoding
:param replacement: (char) replace each char raising encode-decode errs
"""
newstr, err_counter = '', 0
for c in somestr:
try:
newc = c.decode('utf-8').encode(encoding)
newstr = '%s%s' % (newstr, newc)
except UnicodeError as e:
newstr = '%s%s' % (newstr, replacement)
err_counter += 1
if err_counter:
log.warning('WARNING: \t%s character%s failed to be encoded as %s' % (
err_counter, 's' if err_counter > 1 else '', encoding))
log.debug('Unicode Error %s' % e)
return newstr


def encode_for_console(s, encoding=pref_enc, replacement='?'):
if encoding.lower() == 'utf-8':
return s
try:
return s.decode('utf-8').encode(encoding)
except UnicodeError as ue:
log.debug('Encoding(%s): %s' % (encoding, ue))
return _encode_nicely(s, encoding, replacement)


def suggest_missing(miss=None, exclude=[]):
global suggest
sgs = dict(suggest)
Expand All @@ -109,7 +78,7 @@ def suggest_missing(miss=None, exclude=[]):
for k, v in (miss, sgs[miss]) if miss else sgs.items():
if v['active'] and stderr.isatty():
stderr.write('Suggestion: you may like to install %s\n' % k)
stderr.write('\t%s\n' % encode_for_console(v['description']))
stderr.write('\t%s\n' % v['description'])
stderr.write('\tIt is easy, here are the instructions:\n')
stderr.write('\t%s/installation.html%s\n' % (
kamaki_docs, v['url']))
Expand Down Expand Up @@ -368,11 +337,6 @@ def list2file(l, f, depth=1):
# Split input auxiliary


def _parse_with_regex(line, regex):
re_parser = regex_compile(regex)
return (re_parser.split(line), re_parser.findall(line))


def _get_from_parsed(parsed_str):
try:
parsed_str = parsed_str.strip()
Expand All @@ -384,25 +348,24 @@ def _get_from_parsed(parsed_str):


def split_input(line):
if not line:
return []
reg_expr = '\'.*?\'|".*?"|^[\S]*$'
(trivial_parts, interesting_parts) = _parse_with_regex(line, reg_expr)
assert(len(trivial_parts) == 1 + len(interesting_parts))
terms = []
for i, tpart in enumerate(trivial_parts):
part = _get_from_parsed(tpart)
if part:
terms += part
try:
part = _get_from_parsed(interesting_parts[i])
except IndexError:
break
if part:
if tpart and not tpart[-1].endswith(' '):
terms[-1] += ' '.join(part)
else:
if line:
rprs = regex_compile('\'.*?\'|".*?"|^[\S]*$')
trivial_parts, interesting_parts = rprs.split(line), rprs.findall(line)
assert(len(trivial_parts) == 1 + len(interesting_parts))
for i, tpart in enumerate(trivial_parts):
part = _get_from_parsed(tpart)
if part:
terms += part
try:
part = _get_from_parsed(interesting_parts[i])
except IndexError:
break
if part:
if tpart and not tpart[-1].endswith(' '):
terms[-1] += ' '.join(part)
else:
terms += part
return terms


Expand Down

0 comments on commit 7a42aa0

Please sign in to comment.