Skip to content

Commit 6b37c44

Browse files
committed
Fix bug with tmuxp convert. Use prompt, prompt_bool, prompt_choice. pep257 updates
1 parent 18aa882 commit 6b37c44

File tree

2 files changed

+146
-91
lines changed

2 files changed

+146
-91
lines changed

tmuxp/cli.py

Lines changed: 136 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
# -*- coding: utf8 - *-
2-
"""
3-
tmuxp.cli
4-
~~~~~~~~~
2+
"""Command line tool for managing tmux workspaces and tmuxp configurations.
3+
4+
tmuxp.cli
5+
~~~~~~~~~
6+
7+
:copyright: Copyright 2013 Tony Narlock.
8+
:license: BSD, see LICENSE for details
9+
10+
11+
prompt, prompt_bool, prompt_choices
12+
LICENSE: https://github.com/techniq/flask-script/blob/master/LICENSE
513
6-
:copyright: Copyright 2013 Tony Narlock.
7-
:license: BSD, see LICENSE for details
814
"""
915

1016
import os
@@ -16,6 +22,7 @@
1622
from . import config
1723
from distutils.util import strtobool
1824
from . import log, util, exc, WorkspaceBuilder, Server
25+
from .util import ascii_lowercase, input
1926
import pkg_resources
2027

2128
__version__ = pkg_resources.require("tmuxp")[0].version
@@ -28,6 +35,86 @@
2835
teamocil_config_dir = os.path.expanduser('~/.teamocil/')
2936

3037

38+
def prompt(name, default=None):
39+
"""Return user input from command line.
40+
41+
:param name: prompt text
42+
:param default: default value if no input provided.
43+
:rtype: string
44+
45+
"""
46+
47+
prompt = name + (default and ' [%s]' % default or '')
48+
prompt += name.endswith('?') and ' ' or ': '
49+
while True:
50+
rv = input(prompt)
51+
if rv:
52+
return rv
53+
if default is not None:
54+
return default
55+
56+
57+
def prompt_bool(name, default=False, yes_choices=None, no_choices=None):
58+
"""
59+
Return user input from command line and converts to boolean value.
60+
61+
:param name: prompt text
62+
:param default: default value if no input provided.
63+
:param yes_choices: default 'y', 'yes', '1', 'on', 'true', 't'
64+
:param no_choices: default 'n', 'no', '0', 'off', 'false', 'f'
65+
:rtype: bool
66+
67+
"""
68+
69+
yes_choices = yes_choices or ('y', 'yes', '1', 'on', 'true', 't')
70+
no_choices = no_choices or ('n', 'no', '0', 'off', 'false', 'f')
71+
72+
while True:
73+
rv = prompt(name, default and yes_choices[0] or no_choices[0])
74+
if not rv:
75+
return default
76+
if rv.lower() in yes_choices:
77+
return True
78+
elif rv.lower() in no_choices:
79+
return False
80+
81+
82+
def prompt_choices(name, choices, default=None, resolve=ascii_lowercase,
83+
no_choice=('none',)):
84+
"""
85+
Return user input from command line from set of provided choices.
86+
87+
:param name: prompt text
88+
:param choices: list or tuple of available choices. Choices may be
89+
single strings or (key, value) tuples.
90+
:param default: default value if no input provided.
91+
:param no_choice: acceptable list of strings for "null choice"
92+
:rtype: str
93+
94+
"""
95+
96+
_choices = []
97+
options = []
98+
99+
for choice in choices:
100+
if isinstance(choice, basestring):
101+
options.append(choice)
102+
else:
103+
options.append("%s [%s]" % (choice[1], choice[0]))
104+
choice = choice[0]
105+
_choices.append(choice)
106+
107+
while True:
108+
rv = prompt(name + ' - (%s)' % ', '.join(options), default)
109+
if not rv:
110+
return default
111+
rv = resolve(rv)
112+
if rv in no_choice:
113+
return None
114+
if rv in _choices:
115+
return rv
116+
117+
31118
class ConfigCompleter(argcomplete.completers.FilesCompleter):
32119

33120
def __call__(self, prefix, **kwargs):
@@ -75,41 +162,6 @@ def SessionCompleter(prefix, **kwargs):
75162
return [s.get('session_name') for s in t._sessions if s.get('session_name').startswith(prefix)]
76163

77164

78-
def query_yes_no(question, default="yes"):
79-
"""Ask a yes/no question via raw_input() and return their answer.
80-
81-
"question" is a string that is presented to the user.
82-
"default" is the presumed answer if the user just hits <Enter>.
83-
It must be "yes" (the default), "no" or None (meaning
84-
an answer is required of the user).
85-
86-
The "answer" return value is one of "yes" or "no".
87-
88-
License MIT: http://code.activestate.com/recipes/577058/
89-
"""
90-
valid = {"yes": "yes", "y": "yes", "ye": "yes",
91-
"no": "no", "n": "no"}
92-
if default == None:
93-
prompt = " [y/n] "
94-
elif default == "yes":
95-
prompt = " [Y/n] "
96-
elif default == "no":
97-
prompt = " [y/N] "
98-
else:
99-
raise ValueError("invalid default answer: '%s'" % default)
100-
101-
while True:
102-
sys.stdout.write(question + prompt)
103-
choice = raw_input().lower()
104-
if default is not None and choice == '':
105-
return strtobool(default)
106-
elif choice in valid.keys():
107-
return strtobool(valid[choice])
108-
else:
109-
sys.stdout.write("Please respond with 'yes' or 'no' "
110-
"(or 'y' or 'n').\n")
111-
112-
113165
def setupLogger(logger=None, level='INFO'):
114166
'''setup logging for CLI use.
115167
@@ -166,15 +218,15 @@ def build_workspace(config_file, args):
166218
builder.build()
167219

168220
if 'TMUX' in os.environ:
169-
if query_yes_no('Already inside TMUX, load session?'):
221+
if prompt_bool('Already inside TMUX, load session?'):
170222
del os.environ['TMUX']
171223
os.execl(tmux_bin, 'tmux', 'switch-client', '-t', sconfig[
172224
'session_name'])
173225

174226
os.execl(tmux_bin, 'tmux', 'attach-session', '-t', sconfig[
175227
'session_name'])
176228
except exc.TmuxSessionExists as e:
177-
attach_session = query_yes_no(e.message + ' Attach?')
229+
attach_session = prompt_bool(e.message + ' Attach?')
178230

179231
if 'TMUX' in os.environ:
180232
del os.environ['TMUX']
@@ -315,57 +367,50 @@ def subcommand_import_tmuxinator(args):
315367

316368

317369
def subcommand_convert(args):
318-
if args.config:
319-
if '.' in args.config:
320-
args.config.remove('.')
321-
if config.in_cwd():
322-
args.config.append(config.in_cwd()[0])
323-
else:
324-
print('No tmuxp configs found in current directory.')
325-
326-
try:
327-
configfile = args.config
328-
except Exception:
329-
print('Please enter a config')
330370

331-
file_user = os.path.join(config_dir, configfile)
332-
file_cwd = os.path.join(cwd_dir, configfile)
333-
if os.path.exists(file_cwd) and os.path.isfile(file_cwd):
334-
fullfile = os.path.normpath(file_cwd)
335-
filename, ext = os.path.splitext(file_cwd)
336-
elif os.path.exists(file_user) and os.path.isfile(file_user):
371+
try:
372+
configfile = args.config
373+
except Exception:
374+
print('Please enter a config')
375+
376+
file_user = os.path.join(config_dir, configfile)
377+
file_cwd = os.path.join(cwd_dir, configfile)
378+
if os.path.exists(file_cwd) and os.path.isfile(file_cwd):
379+
fullfile = os.path.normpath(file_cwd)
380+
filename, ext = os.path.splitext(file_cwd)
381+
elif os.path.exists(file_user) and os.path.isfile(file_user):
382+
383+
fullfile = os.path.normpath(file_user)
384+
filename, ext = os.path.splitext(file_user)
385+
else:
386+
logger.error('%s not found.' % configfile)
387+
return
337388

338-
fullfile = os.path.normpath(file_user)
339-
filename, ext = os.path.splitext(file_user)
340-
else:
341-
logger.error('%s not found.' % configfile)
342-
return
343-
344-
if 'json' in ext:
345-
if query_yes_no('convert to <%s> to yaml?' % (fullfile)):
346-
configparser = kaptan.Kaptan()
347-
configparser.import_config(configfile)
348-
newfile = fullfile.replace(ext, '.yaml')
349-
newconfig = configparser.export(
350-
'yaml', indent=2, default_flow_style=False
351-
)
352-
if query_yes_no('write config to %s?' % (newfile)):
353-
buf = open(newfile, 'w')
354-
buf.write(newconfig)
355-
buf.close()
356-
print ('written new config to %s' % (newfile))
357-
elif 'yaml' in ext:
358-
if query_yes_no('convert to <%s> to json?' % (fullfile)):
359-
configparser = kaptan.Kaptan()
360-
configparser.import_config(configfile)
361-
newfile = fullfile.replace(ext, '.json')
362-
newconfig = configparser.export('json', indent=2)
363-
print(newconfig)
364-
if query_yes_no('write config to <%s>?' % (newfile)):
365-
buf = open(newfile, 'w')
366-
buf.write(newconfig)
367-
buf.close()
368-
print ('written new config to <%s>.' % (newfile))
389+
if 'json' in ext:
390+
if prompt_bool('convert to <%s> to yaml?' % (fullfile)):
391+
configparser = kaptan.Kaptan()
392+
configparser.import_config(configfile)
393+
newfile = fullfile.replace(ext, '.yaml')
394+
newconfig = configparser.export(
395+
'yaml', indent=2, default_flow_style=False
396+
)
397+
if prompt_bool('write config to %s?' % (newfile)):
398+
buf = open(newfile, 'w')
399+
buf.write(newconfig)
400+
buf.close()
401+
print ('written new config to %s' % (newfile))
402+
elif 'yaml' in ext:
403+
if prompt_bool('convert to <%s> to json?' % (fullfile)):
404+
configparser = kaptan.Kaptan()
405+
configparser.import_config(configfile)
406+
newfile = fullfile.replace(ext, '.json')
407+
newconfig = configparser.export('json', indent=2)
408+
print(newconfig)
409+
if prompt_bool('write config to <%s>?' % (newfile)):
410+
buf = open(newfile, 'w')
411+
buf.write(newconfig)
412+
buf.close()
413+
print ('written new config to <%s>.' % (newfile))
369414

370415

371416
def subcommand_attach_session(args):

tmuxp/util.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
logger = logging.getLogger(__name__)
2424

25+
PY2 = sys.version_info[0] == 2
26+
2527

2628
class tmux(object):
2729

@@ -266,3 +268,11 @@ def oh_my_zsh_auto_title():
266268
unicode = unicode
267269
bytes = str
268270
basestring = basestring
271+
272+
273+
if not PY2:
274+
input = input
275+
from string import ascii_lowercase
276+
else:
277+
input = raw_input
278+
from string import lower as ascii_lowercase

0 commit comments

Comments
 (0)