Skip to content

Commit 9acd37b

Browse files
committed
Adds Shell / Moves fixtures
This commit adds a shell that can be started by simply calling 'slcli' with no command. The shell can be exited using ctrl + d. The shell has autocomplete. The shell probably needs a way to set global options (--fixtures, --debug, --proxy, --config, --timings, etc). In the future, this mode can be used to prompt for user input. Fixtures were moved to a higher level to avoid needing test dependencies to use fixtures.
1 parent 751b3d4 commit 9acd37b

57 files changed

Lines changed: 117 additions & 17 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SoftLayer/CLI/core.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
SoftLayer.core
3-
~~~~~~~~~~~~~~
2+
SoftLayer.CLI.core
3+
~~~~~~~~~~~~~~~~~~
44
Core for the SoftLayer CLI
55
66
:license: MIT, see LICENSE for more details.
@@ -71,6 +71,7 @@ def get_command(self, ctx, name):
7171
username and api_key need to be configured. The easiest way to do that is to
7272
use: 'slcli setup'""",
7373
cls=CommandLoader,
74+
invoke_without_command=True,
7475
context_settings={'help_option_names': ['-h', '--help']})
7576
@click.option('--format',
7677
default=DEFAULT_FORMAT,
@@ -109,7 +110,8 @@ def get_command(self, ctx, name):
109110
help="Use fixtures instead of actually making API calls")
110111
@click.version_option(prog_name="slcli (SoftLayer Command-line)")
111112
@environment.pass_env
112-
def cli(env,
113+
@click.pass_context
114+
def cli(ctx, env,
113115
format='table',
114116
config=None,
115117
debug=0,
@@ -151,6 +153,10 @@ def cli(env,
151153
client.transport = SoftLayer.TimingTransport(client.transport)
152154
env.client = client
153155

156+
if ctx.invoked_subcommand is None:
157+
from SoftLayer.CLI import shell
158+
shell.main(env)
159+
154160

155161
@cli.resultcallback()
156162
@environment.pass_env
@@ -171,12 +177,12 @@ def output_result(env, result, timings=False, **kwargs):
171177
env.err(env.fmt(timing_table))
172178

173179

174-
def main():
180+
def main(**kwargs):
175181
"""Main program. Catches several common errors and displays them nicely."""
176182
exit_status = 0
177183

178184
try:
179-
cli.main()
185+
cli.main(**kwargs)
180186
except SoftLayer.SoftLayerAPIError as ex:
181187
if 'invalid api token' in ex.faultString.lower():
182188
print("Authentication Failed: To update your credentials,"

SoftLayer/CLI/shell.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""
2+
SoftLayer.CLI.shell
3+
~~~~~~~~~~~~~~~~~~~
4+
An interactive shell which exposes the CLI
5+
6+
:license: MIT, see LICENSE for more details.
7+
"""
8+
from __future__ import print_function
9+
from __future__ import unicode_literals
10+
import shlex
11+
import sys
12+
import traceback
13+
14+
import click
15+
from prompt_toolkit import completion
16+
from prompt_toolkit import shortcuts
17+
18+
from SoftLayer.CLI import core
19+
20+
# pylint: disable=broad-except
21+
22+
23+
def main(env):
24+
"""Main entry-point for the shell."""
25+
exit_code = 0
26+
while True:
27+
try:
28+
line = shortcuts.get_input("(%s)> " % exit_code,
29+
completer=ShellCompleter())
30+
try:
31+
args = shlex.split(line)
32+
except ValueError as ex:
33+
print("Invalid Command: %s" % ex)
34+
continue
35+
36+
core.main(args=args, obj=env)
37+
except SystemExit as ex:
38+
exit_code = ex.code
39+
except KeyboardInterrupt:
40+
exit_code = 1
41+
except EOFError:
42+
return
43+
except Exception:
44+
exit_code = 1
45+
traceback.print_exc(file=sys.stderr)
46+
47+
48+
class ShellCompleter(completion.Completer):
49+
"""Completer for the shell."""
50+
51+
def get_completions(self, document, complete_event):
52+
"""Returns an iterator of completions for the shell."""
53+
try:
54+
parts = shlex.split(document.text_before_cursor)
55+
except ValueError:
56+
return []
57+
58+
return _click_generator(core.cli, parts)
59+
60+
61+
def _click_generator(root, parts):
62+
"""Completer generator for click applications."""
63+
location = root
64+
incomplete = ''
65+
for part in parts:
66+
incomplete = part
67+
68+
if not part[0:2].isalnum():
69+
continue
70+
71+
try:
72+
next_location = location.get_command(click.Context(location),
73+
part)
74+
if next_location is not None:
75+
location = next_location
76+
incomplete = ''
77+
except AttributeError:
78+
break
79+
80+
options = []
81+
if incomplete and not incomplete[0:2].isalnum():
82+
for param in location.params:
83+
if not isinstance(param, click.Option):
84+
continue
85+
options.extend(param.opts)
86+
options.extend(param.secondary_opts)
87+
elif isinstance(location, (click.MultiCommand, click.core.Group)):
88+
options.extend(location.list_commands(click.Context(location)))
89+
90+
# yield all collected options that starts with the incomplete section
91+
for option in options:
92+
if option.startswith(incomplete):
93+
yield completion.Completion(option, -len(incomplete))
File renamed without changes.
File renamed without changes.

SoftLayer/testing/fixtures/SoftLayer_Billing_Order_Quote.py renamed to SoftLayer/fixtures/SoftLayer_Billing_Order_Quote.py

File renamed without changes.
File renamed without changes.

SoftLayer/testing/fixtures/SoftLayer_Dns_Domain_ResourceRecord.py renamed to SoftLayer/fixtures/SoftLayer_Dns_Domain_ResourceRecord.py

File renamed without changes.
File renamed without changes.
File renamed without changes.

SoftLayer/testing/fixtures/SoftLayer_Location_Datacenter.py renamed to SoftLayer/fixtures/SoftLayer_Location_Datacenter.py

File renamed without changes.

0 commit comments

Comments
 (0)