Skip to content

Commit

Permalink
Defer subcommand context creation until later.
Browse files Browse the repository at this point in the history
This fixes a regression that was introduced with the 3.0
release.  This broke well established patters and was never
intended to break.

This fixes pallets#200
  • Loading branch information
mitsuhiko committed Aug 13, 2014
1 parent 85dec99 commit d88d0a7
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Version 3.1

(bugfix release, release date to be decided)

- Fixed a regression that caused contexts of subcommands to be
created before the parent command was invoked which was a
regression from earlier Click versions.

Version 3.0
-----------

Expand Down
32 changes: 16 additions & 16 deletions click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -883,33 +883,33 @@ def _process_result(value):
# single command but we also inform the current context about the
# name of the command to invoke.
if not self.chain:
sub_ctx = self.handle_subcommand(ctx, args)
ctx.invoked_subcommands = [sub_ctx.info_name]

# Make sure the context is entered so we do not clean up
# resources until the result processor has worked.
with ctx:
Command.invoke(self, ctx)
sub_ctx = self.handle_subcommand(ctx, args)
ctx.invoked_subcommands = [sub_ctx.info_name]
with sub_ctx:
return _process_result(sub_ctx.command.invoke(sub_ctx))

# Otherwise we make every single context and invoke them in a
# chain. In that case the return value to the result processor
# is the list of all invoked subcommand's results.
contexts = []
while args:
sub_ctx = self.handle_subcommand(ctx, args, allow_extra_args=True,
allow_interspersed_args=False)
contexts.append(sub_ctx)
args = sub_ctx.args

# Now that we have all contexts, we can invoke them.
ctx.invoked_subcommands = [x.info_name for x in contexts]

# Make sure the context is entered so we do not clean up
# resources until the result processor has worked.
with ctx:
Command.invoke(self, ctx)

# Otherwise we make every single context and invoke them in a
# chain. In that case the return value to the result processor
# is the list of all invoked subcommand's results.
contexts = []
while args:
sub_ctx = self.handle_subcommand(ctx, args, allow_extra_args=True,
allow_interspersed_args=False)
contexts.append(sub_ctx)
args = sub_ctx.args

# Now that we have all contexts, we can invoke them.
ctx.invoked_subcommands = [x.info_name for x in contexts]

rv = []
for sub_ctx in contexts:
with sub_ctx:
Expand Down
20 changes: 20 additions & 0 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,23 @@ def test_callback(args, filename, verbose):
' -f FILE, --file=FILE write report to FILE',
' -q, --quiet don\'t print status messages to stdout',
]


def test_object_propagation(runner):
for chain in False, True:
@click.group(chain=chain)
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
if ctx.obj is None:
ctx.obj = {}
ctx.obj['DEBUG'] = debug

@cli.command()
@click.pass_context
def sync(ctx):
click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))

result = runner.invoke(cli, ['sync'])
assert result.exception is None
assert result.output == 'Debug is off\n'

0 comments on commit d88d0a7

Please sign in to comment.