diff --git a/CHANGES b/CHANGES index 0c6f25f5b..050bfaf77 100644 --- a/CHANGES +++ b/CHANGES @@ -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 ----------- diff --git a/click/core.py b/click/core.py index 655b0acf1..99c1f495b 100644 --- a/click/core.py +++ b/click/core.py @@ -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: diff --git a/tests/test_commands.py b/tests/test_commands.py index 1398437d2..f7ccbbb27 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -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'