Skip to content

Commit 44c1c1a

Browse files
committed
Support application-wide options
1 parent 28f1a95 commit 44c1c1a

File tree

2 files changed

+61
-48
lines changed

2 files changed

+61
-48
lines changed

flask_script/__init__.py

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
__all__ = ["Command", "Shell", "Server", "Manager", "Group", "Option",
1717
"prompt", "prompt_pass", "prompt_bool", "prompt_choices"]
1818

19+
safe_actions = (argparse._StoreAction,
20+
argparse._StoreConstAction,
21+
argparse._StoreTrueAction,
22+
argparse._StoreFalseAction,
23+
argparse._AppendAction,
24+
argparse._AppendConstAction,
25+
argparse._CountAction)
26+
1927

2028
class Manager(object):
2129
"""
@@ -131,12 +139,12 @@ def create_parser(self, prog, parser=None):
131139
for option in self.get_options():
132140
parser.add_argument(*option.args, **option.kwargs)
133141

134-
subparsers = parser.add_subparsers()
142+
subparsers = parser.add_subparsers() # dest='subparser_'+prog)
135143
for name, command in self._commands.iteritems():
136144
description = getattr(command, 'description',
137145
'Perform command ' + name)
138146
p = subparsers.add_parser(name, help=description, usage=description)
139-
command.create_parser(name, parser=p)
147+
p = command.create_parser(name, parser=p)
140148

141149
return parser
142150

@@ -305,34 +313,43 @@ def _handle(self, app, *args, **kwargs):
305313
self.print_usage()
306314
return 1
307315

308-
def handle(self, prog, name, args=None):
316+
def handle(self, prog, args=None):
309317

310318
args = list(args or [])
311319

312-
try:
313-
command = self._commands[name]
314-
except KeyError:
315-
raise InvalidCommand("Command %s not found" % name)
316-
317320
app_parser = self.create_parser(prog)
318-
app_namespace, remaining_args = app_parser.parse_known_args([name] + args)
321+
app_namespace, remaining_args = app_parser.parse_known_args(args)
319322

320323
kwargs = app_namespace.__dict__
321324
handle = kwargs['func_handle']
322325
del kwargs['func_handle']
323-
app = self.create_app(**app_namespace.__dict__)
324326

325-
# get command from bounded handle function (py2.7+)
327+
## get only safe config options
328+
app_config_keys = [action.dest for action in app_parser._actions
329+
if action.__class__ in safe_actions]
330+
331+
## pass only safe app config keys
332+
app_config = dict((k, v) for k, v in kwargs.iteritems()
333+
if k in app_config_keys)
334+
335+
## remove application config keys from handle kwargs
336+
kwargs = dict((k, v) for k, v in kwargs.iteritems()
337+
if k not in app_config_keys)
338+
339+
## get command from bounded handle function (py2.7+)
326340
command = handle.__self__
327341
if getattr(command, 'capture_all_args', False):
328342
positional_args = [remaining_args]
329343
else:
330344
if len(remaining_args):
331345
# raise correct exception
332346
# FIXME maybe change capture_all_args flag
333-
app_parser.parse_args([name] + args)
347+
app_parser.parse_args(args)
334348
# sys.exit(2)
349+
pass
335350
positional_args = []
351+
352+
app = self.create_app(**app_config)
336353
return handle(app, *positional_args, **kwargs)
337354

338355
def run(self, commands=None, default_command=None):
@@ -351,21 +368,14 @@ def run(self, commands=None, default_command=None):
351368
if commands:
352369
self._commands.update(commands)
353370

354-
try:
355-
try:
356-
command = sys.argv[1]
357-
except IndexError:
358-
command = default_command
359-
360-
if command is None:
361-
raise InvalidCommand("Please provide a command:")
362-
363-
result = self.handle(sys.argv[0], command, sys.argv[2:])
371+
if default_command is not None and len(sys.argv) == 1:
372+
sys.argv.append(default_command)
364373

365-
sys.exit(result or 0)
366-
367-
except InvalidCommand, e:
368-
print e
369-
self.print_usage()
374+
try:
375+
result = self.handle(sys.argv[0], sys.argv[1:])
376+
except SystemExit as e:
377+
result = e.code
378+
except Exception as e:
379+
raise e
370380

371-
sys.exit(1)
381+
sys.exit(result or 0)

tests.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ def run(command_line, manager_run, capture_stderr=False):
2424
except SystemExit as e:
2525
exit_code = e.code
2626
finally:
27+
out = sys.stdout.getvalue()
28+
if capture_stderr:
29+
out += sys.stderr.getvalue()
2730
sys.stderr = sys_stderr_orig
2831

29-
return sys.stdout.getvalue(), exit_code
32+
return out, exit_code
3033

3134

3235
class SimpleCommand(Command):
@@ -257,23 +260,18 @@ def hello_again(name, url=None):
257260
stdout, code = run('manage.py hello_again --name=joe --url=reddit.com', lambda: manager.run())
258261
assert 'hello joe from reddit.com' in stdout
259262

260-
def test_global_option_provided_before_and_after_command(self):
263+
def test_global_option_provided_before_command(self):
261264

262265
manager = Manager(self.app)
263266
manager.add_option('-c', '--config', dest='config_name', required=False, default='Development')
264267
manager.add_command('simple', SimpleCommand())
265268

266269
assert isinstance(manager._commands['simple'], SimpleCommand)
267270

268-
stdout, code = run('manage.py simple -c Development', lambda: manager.run())
271+
stdout, code = run('manage.py -c Development simple', lambda: manager.run())
269272
assert code == 0
270273
assert 'OK' in stdout
271274

272-
# TODO: Allow swapping '-c Development' option and 'simple' command
273-
# stdout, code = run('manage.py -c Development simple', lambda: manager.run())
274-
# assert code == 0
275-
# assert 'OK' in stdout
276-
277275
def test_get_usage(self):
278276

279277
manager = Manager(self.app)
@@ -300,7 +298,7 @@ def test_run_existing_command(self):
300298
def test_run_non_existant_command(self):
301299

302300
manager = Manager(self.app)
303-
self.assertRaises(InvalidCommand, manager.handle, 'manage.py', 'simple')
301+
self.assertRaises(SystemExit, manager.handle, 'manage.py', 'simple')
304302

305303
def test_run_existing(self):
306304

@@ -324,15 +322,15 @@ def test_run_not_existing(self):
324322
manager = Manager(self.app)
325323

326324
stdout, code = run('manage.py simple', lambda: manager.run())
327-
assert code == 1
325+
assert code == 2
328326
assert 'OK' not in stdout
329327

330328
def test_run_no_name(self):
331329

332330
manager = Manager(self.app)
333331

334332
stdout, code = run('manage.py', lambda: manager.run())
335-
assert code == 1
333+
assert code == 2
336334

337335
def test_run_good_options(self):
338336

@@ -372,7 +370,7 @@ def test_init_with_flask_instance(self):
372370
assert callable(manager.app)
373371

374372
def test_init_with_callable(self):
375-
manager = Manager(lambda: app)
373+
manager = Manager(lambda: self.app)
376374
assert callable(manager.app)
377375

378376
def test_raise_index_error(self):
@@ -384,7 +382,7 @@ def error():
384382
raise IndexError()
385383

386384
try:
387-
self.assertRaises(IndexError, manager.run, default_command='error')
385+
self.assertRaises(IndexError, run, 'manage.py error', lambda: manager.run())
388386
except SystemExit, e:
389387
assert e.code == 1
390388

@@ -442,7 +440,7 @@ def test_submanager_has_options(self):
442440
assert code == 0
443441
assert 'OK' in stdout
444442

445-
stdout, code = run('manage.py sub_manager simple -c Development', lambda: manager.run())
443+
stdout, code = run('manage.py -c Development sub_manager simple', lambda: manager.run())
446444
assert code == 0
447445
assert 'OK' in stdout
448446

@@ -453,9 +451,9 @@ def test_manager_usage_with_submanager(self):
453451
manager = Manager(self.app)
454452
manager.add_command('sub_manager', sub_manager)
455453

456-
stdout, code = run('manage.py', lambda: manager.run())
457-
assert code == 1
458-
assert 'sub_manager Example sub-manager' in stdout
454+
stdout, code = run('manage.py -h', lambda: manager.run())
455+
assert code == 0
456+
assert 'Example sub-manager' in stdout
459457

460458
def test_submanager_usage(self):
461459

@@ -465,9 +463,14 @@ def test_submanager_usage(self):
465463
manager = Manager(self.app)
466464
manager.add_command('sub_manager', sub_manager)
467465

468-
stdout, code = run('manage.py sub_manager', lambda: manager.run())
469-
assert code == 1
470-
assert 'simple simple command' in stdout
466+
stdout, code = run('manage.py sub_manager', lambda: manager.run(),
467+
capture_stderr=True)
468+
assert code == 2
469+
assert 'too few arguments' in stdout
470+
471+
stdout, code = run('manage.py sub_manager -h', lambda: manager.run())
472+
assert code == 0
473+
assert 'simple command' in stdout
471474

472475
def test_submanager_has_no_default_commands(self):
473476

0 commit comments

Comments
 (0)