Skip to content

Commit ac94959

Browse files
author
Clark Perkins
committed
Updated generator to use click
1 parent ea0dc77 commit ac94959

File tree

3 files changed

+42
-82
lines changed

3 files changed

+42
-82
lines changed
Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,43 @@
11
from __future__ import print_function
22

3-
import argparse
43
import os
54
import json
65
import sys
76

8-
from stackdio.cli.blueprints.generator import BlueprintException, BlueprintGenerator
9-
10-
11-
def main():
12-
parser = argparse.ArgumentParser(
13-
description='invoke the stackdio blueprint generator')
7+
import click
148

15-
parser.add_argument('template_file',
16-
help='The template file to generate from')
9+
from stackdio.cli.blueprints.generator import BlueprintException, BlueprintGenerator
1710

18-
parser.add_argument('var_files',
19-
metavar='var_file',
20-
nargs='*',
21-
help='The variable files with your custom config. They will be loaded '
22-
'from left to right, so variables in the rightmost var files will '
23-
'override those in var files to the left.')
2411

25-
parser.add_argument('-p', '--prompt',
26-
action='store_true',
27-
help='Prompt user for missing variables')
12+
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
2813

29-
parser.add_argument('-d', '--debug',
30-
action='store_true',
31-
help='Print out json string before parsing the json')
3214

33-
args = parser.parse_args()
15+
@click.command(context_settings=CONTEXT_SETTINGS)
16+
@click.argument('template_file')
17+
@click.argument('var_files', nargs=-1, type=click.File('r'))
18+
@click.option('-p', '--prompt', is_flag=True, default=False,
19+
help='Prompt user for missing variables')
20+
@click.option('-d', '--debug', is_flag=True, default=False,
21+
help='Print out json string before parsing the json')
22+
def main(template_file, var_files, prompt, debug):
3423

3524
try:
3625
# Throw all output to stderr
3726
gen = BlueprintGenerator([os.path.curdir,
3827
os.path.join(os.path.curdir, 'templates'),
39-
os.path.dirname(os.path.abspath(args.template_file))],
28+
os.path.dirname(os.path.abspath(template_file))],
4029
output_stream=sys.stderr)
4130

4231
# Generate the blueprint
43-
blueprint = gen.generate(args.template_file,
44-
var_files=args.var_files,
45-
prompt=args.prompt,
46-
debug=args.debug)
32+
blueprint = gen.generate(template_file,
33+
var_files=var_files,
34+
prompt=prompt,
35+
debug=debug)
4736
except BlueprintException:
48-
sys.exit(1)
37+
raise click.Abort('Error processing blueprint')
4938

50-
print(json.dumps(blueprint, indent=2))
39+
click.echo(json.dumps(blueprint, indent=2))
5140

5241

5342
if __name__ == '__main__':
54-
try:
55-
main()
56-
except KeyboardInterrupt:
57-
sys.stderr.write('Aborting...\n')
58-
sys.exit(1)
43+
main()

stackdio/cli/blueprints/generator.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,14 @@
44
import os
55
import json
66

7+
import click
78
import yaml
89
from jinja2 import Environment, FileSystemLoader, StrictUndefined, meta
910
from jinja2.exceptions import TemplateNotFound, TemplateSyntaxError, UndefinedError
1011
from jinja2.nodes import Assign, Block, Const, If, Not
1112
from jinja2.filters import do_replace, evalcontextfilter
1213

1314

14-
COLORS = {
15-
'brown': '\033[0;33m',
16-
'green': '\033[0;32m',
17-
'red': '\033[01;31m',
18-
'endc': '\033[0m',
19-
}
20-
21-
2215
class BlueprintException(Exception):
2316
pass
2417

@@ -67,9 +60,8 @@ def error_exit(self, message, newlines=1):
6760
:param newlines: the number of newlines to print at the end
6861
:return: None
6962
"""
70-
self.out_stream.write('{0}{1}{2}'.format(COLORS['red'], message, COLORS['endc']))
71-
for i in range(0, newlines):
72-
self.out_stream.write('\n')
63+
click.secho(message, file=self.out_stream, nl=False, fg='red')
64+
click.echo('\n' * newlines, nl=False)
7365
raise BlueprintException()
7466

7567
def warning(self, message, newlines=1):
@@ -79,17 +71,16 @@ def warning(self, message, newlines=1):
7971
:param newlines: The number of newlines to print at the end
8072
:return: None
8173
"""
82-
self.out_stream.write('{0}{1}{2}'.format(COLORS['brown'], message, COLORS['endc']))
83-
for i in range(0, newlines):
84-
self.out_stream.write('\n')
74+
click.secho(message, file=self.out_stream, nl=False, fg='yellow')
75+
click.echo('\n' * newlines, nl=False)
8576

8677
def prompt(self, message):
8778
"""
8879
Prompts the user for an input. Prints the prompt to the configured output stream in green
8980
:param message: the prompt message
9081
:return: the value the user inputted
9182
"""
92-
self.out_stream.write('{0}{1}{2}'.format(COLORS['green'], message, COLORS['endc']))
83+
click.secho(message, file=self.out_stream, nl=False, fg='green')
9384
raw = sys.stdin.readline().strip()
9485

9586
# This should work nicely - if yaml can't parse it properly, then it should be fine to just
@@ -202,7 +193,7 @@ def generate(self, template_file, var_files=(), variables=None, prompt=False, de
202193
203194
:param template_file: The relative location of the template. It must be in one of the
204195
directories you specified when creating the Generator object.
205-
:param var_file: The location of the variable file (relative or absolute)
196+
:param var_files: The location of the variable file(s) (relative or absolute)
206197
:param variables: A dict of variables to put in the template.
207198
:param prompt: Option to prompt for missing variables
208199
:param debug: Print the output of the template before trying to parse it as JSON
@@ -215,7 +206,7 @@ def generate(self, template_file, var_files=(), variables=None, prompt=False, de
215206

216207
context = {}
217208
for var_file in var_files:
218-
yaml_parsed = yaml.safe_load(open(var_file, 'r'))
209+
yaml_parsed = yaml.safe_load(var_file)
219210
if yaml_parsed:
220211
context.update(yaml_parsed)
221212

@@ -287,9 +278,9 @@ def generate(self, template_file, var_files=(), variables=None, prompt=False, de
287278
template_json = template.render(**context)
288279

289280
if debug:
290-
print('\n')
291-
print(template_json)
292-
print('\n')
281+
click.echo('\n')
282+
click.echo(template_json)
283+
click.echo('\n')
293284

294285
# Return a dict object rather than a string
295286
return json.loads(template_json)

stackdio/cli/mixins/blueprints.py

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
from __future__ import print_function
21

32
import json
43
import os
5-
import argparse
6-
import sys
74

85
import click
96
import yaml
10-
from cmd2 import Cmd
117

12-
from stackdio.client.exceptions import StackException
138
from stackdio.cli.blueprints.generator import BlueprintGenerator, BlueprintException
149
from stackdio.cli.utils import print_summary
1510

@@ -20,6 +15,9 @@ class BlueprintNotFound(Exception):
2015

2116
@click.group()
2217
def blueprints():
18+
"""
19+
Perform actions on blueprints
20+
"""
2321
pass
2422

2523

@@ -124,11 +122,6 @@ def create_blueprint(obj, mapping, template, var_file, no_prompt):
124122
"""
125123
Create a blueprint
126124
"""
127-
print(mapping)
128-
print(template)
129-
print(var_file)
130-
print(no_prompt)
131-
132125
client = obj['client']
133126

134127
if not template and not mapping:
@@ -165,19 +158,13 @@ def create_blueprint(obj, mapping, template, var_file, no_prompt):
165158

166159
@blueprints.command(name='create-all')
167160
@click.pass_obj
168-
@click.option('--no-prompt', is_flag=True, default=True,
169-
help='Don\'t prompt to create all blueprints')
170-
def create_all_blueprints(obj, no_prompt):
161+
@click.confirmation_option('-y', '--yes', prompt='Really create all blueprints?')
162+
def create_all_blueprints(obj):
171163
"""
172164
Create all the blueprints in the map file
173165
"""
174166
client = obj['client']
175167

176-
if no_prompt:
177-
really = raw_input('Really create all blueprints (y/n)? ')
178-
if really not in ['Y', 'y']:
179-
return
180-
181168
blueprint_dir = os.path.expanduser(client.config['blueprint_dir'])
182169
mapping = yaml.safe_load(open(os.path.join(blueprint_dir, 'mappings.yaml'), 'r'))
183170

@@ -192,14 +179,14 @@ def create_all_blueprints(obj, no_prompt):
192179

193180

194181
def _get_blueprint_id(client, blueprint_title):
195-
blueprints = client.search_blueprints(title=blueprint_title)
182+
found_blueprints = client.search_blueprints(title=blueprint_title)
196183

197-
if len(blueprints) == 0:
198-
raise click.UsageError('Blueprint [{0}] does not exist'.format(blueprint_title))
199-
elif len(blueprints) > 1:
200-
raise click.UsageError('Blueprint [{0}] does not exist'.format(blueprint_title))
184+
if len(found_blueprints) == 0:
185+
raise click.Abort('Blueprint "{0}" does not exist'.format(blueprint_title))
186+
elif len(found_blueprints) > 1:
187+
raise click.Abort('Multiple blueprints matching "{0}" were found'.format(blueprint_title))
201188
else:
202-
return blueprints[0]['id']
189+
return found_blueprints[0]['id']
203190

204191

205192
@blueprints.command(name='delete')
@@ -213,10 +200,7 @@ def delete_blueprint(obj, title):
213200

214201
blueprint_id = _get_blueprint_id(client, title)
215202

216-
really = raw_input('Really delete blueprint {0} (y/n)? '.format(title))
217-
if really not in ['y', 'Y']:
218-
click.echo('Aborting deletion')
219-
return
203+
click.confirm('Really delete blueprint {0}?'.format(title), abort=True)
220204

221205
click.echo('Deleting {0}'.format(title))
222206
client.delete_blueprint(blueprint_id)

0 commit comments

Comments
 (0)