Skip to content

Fix get_parameter_source() during type conversion and eager callbacks#3484

Open
yurishevtsov wants to merge 1 commit into
pallets:mainfrom
yurishevtsov:fix-parameter-source-before-process-value
Open

Fix get_parameter_source() during type conversion and eager callbacks#3484
yurishevtsov wants to merge 1 commit into
pallets:mainfrom
yurishevtsov:fix-parameter-source-before-process-value

Conversation

@yurishevtsov
Copy link
Copy Markdown

@yurishevtsov yurishevtsov commented May 20, 2026

Summary

Click 8.4.0 moved set_parameter_source() to after process_value() when fixing flag-group arbitration (#3403, #3404). That broke a case wasn't meant to change: anything that calls get_parameter_source() during type conversion or in an eager callback saw None, even though consume_value() had already figured out where the value came from (#3458).

This is affecting Flask: with Click 8.4.0, FLASK_DEBUG=1 in the environment was ignored because _set_debug runs as an eager callback and could no longer see that the --debug flag was only at its default (pallets/flask#6025).

The fix is to record the source right after consume_value() and before process_value(), matching 8.3.3 timing for callbacks and custom types. Flag-group arbitration is the same as today.

There is a second, narrower issue on the same code path: recording the source early means a losing option in a feature-switch group briefly overwrites _parameter_source before arbitration runs. I am solving it by restoring the previous source when an option loses the slot. That path is easy to miss in tests (shared opts may give the loser the same source as the winner), so there is a dedicated case in test_bool_flag_group_parameter_source. More detail is in the comments around handle_parse_result.

fixes #3458

Tests added / updated

  • tests/test_defaults.py — issue repro (ParamType.convert), eager callback, Flask-style _set_debug guard
  • tests/test_options.pytest_bool_flag_group_parameter_source (includes a loser-restore case with envvar, not only CLI + default_map)

Record parameter source on the context immediately after consume_value(),
before process_value() runs. In 8.4.0, set_parameter_source() was deferred
until after type conversion and flag-group arbitration (0f71fe7, pallets#3403), so
get_parameter_source() returned None inside ParamType.convert() and eager
callbacks (pallets#3458).

When several options share one parameter name, the losing option still sets a
provisional source before process_value(); restore the previous source if
arbitration rejects it so the winner's origin is not replaced unintentionally.
@Rowlando13 Rowlando13 requested a review from kdeldycke May 20, 2026 00:10
Comment thread src/click/core.py
# Record the source before processing so eager callbacks and type
# conversion can inspect it. Restored after arbitration if this
# option loses a feature-switch group.
ctx.set_parameter_source(self.name, source)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restores 8.3.3 timing so get_parameter_source() works inside convert / eager callbacks (#3458).

#3404 moved this to the end of handle_parse_result, which caused the regression.

Comment thread src/click/core.py
ctx.set_parameter_source(self.name, source)
elif existing_source is not None:
# Lost arbitration; restore the winning option's source.
ctx.set_parameter_source(self.name, existing_source)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option already wrote its provisional source above; if it loses arbitration, put the winner’s source back so _parameter_source matches ctx.param.

Comment thread src/click/core.py
)

if is_winner:
ctx.set_parameter_source(self.name, source)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Winner already has the right source from the early write. Only params need updating here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_parameter_source() returns None in 8.4

1 participant