Skip to content

Introduce stacklevel-aware custom warnings#6293

Merged
c-bata merged 8 commits intooptuna:masterfrom
gen740:warn_stacklevel
Nov 4, 2025
Merged

Introduce stacklevel-aware custom warnings#6293
c-bata merged 8 commits intooptuna:masterfrom
gen740:warn_stacklevel

Conversation

@gen740
Copy link
Copy Markdown
Member

@gen740 gen740 commented Oct 3, 2025

Motivation

For better user experience, warnings should point to the user’s code, not Optuna’s internal code.

For example, with the following code in the current master branch, the warning points to non-user code. In such cases, it is difficult for users to identify the actual source in their own code.

import optuna


study = optuna.create_study()


trial = study.ask()

trial.suggest_float("x", 0, 1)
trial.suggest_float("x", 0, 10)

output

[I 2025-10-03 15:50:51,639] A new study created in memory with name: no-name-e267bc38-d5bf-45f8-b97a-4514fa0d9f67
/Users/gen/work/Optuna/optuna/optuna/trial/_trial.py:682: RuntimeWarning: Inconsistent parameter values for distribution with name "x"! This might be a configuration mistake. Optuna allows to call the same distribution with the same name more than once in a trial. When the parameter values are inconsistent optuna only uses the values of the first call and ignores all following. Using these values: {'step': None, 'low': 0.0, 'high': 1.0, 'log': False}
  warnings.warn(

As discussed in #6227, warnings.warn accepts a second argument stacklevel, which allows skipping a certain number of frames in the stack. However, passing the correct stack level everywhere is difficult, since different call paths may require different values.

To address this, I implemented a wrapper that determines the stack level based on the module path. This wrapper calculates the appropriate stack level relative to the user’s code and passes it to warnings.warn.

Starting from Python 3.12, warnings.warn provides the skip_file_prefixes argument, which can skip stack frames belonging to files with specific prefixes. However, this argument is not available in versions earlier than 3.12. Therefore, I implemented a custom wrapper inspired by the skip_file_prefixes mechanism.

Description of the changes

  • Add _warnings.py.
  • Replace import warnings to from optuna import _warnings as warnings.

@c-bata c-bata marked this pull request as ready for review October 6, 2025 05:53
@c-bata c-bata added the enhancement Change that does not break compatibility and not affect public interfaces, but improves performance. label Oct 6, 2025
@c-bata
Copy link
Copy Markdown
Member

c-bata commented Oct 6, 2025

@kAIto47802 Could you review this PR?

Copy link
Copy Markdown
Collaborator

@kAIto47802 kAIto47802 left a comment

Choose a reason for hiding this comment

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

Thank you for the PR! I left some comments. PTRL :octocat:

Also, the stack level is currently specified in the decorators to point to the user code, which I think is no longer necessary with this PR. Therefore, how about removing these specifications?
In the current implementation, the stack level has increased by one, so it no longer points to the user code but instead points inside the Optuna internal decorator. If we don’t remove these specifications, we need to increase their stack level by one.

These specifications can be found in the following files:

  • optuna/_convert_positional_args.py
  • optuna/_deprecated.py
  • optuna/_experimental.py

@kAIto47802
Copy link
Copy Markdown
Collaborator

Also, the change to from optuna import _warnings as warnings is missing in optuna/samplers/_base.py.

@github-actions
Copy link
Copy Markdown
Contributor

This pull request has not seen any recent activity.

@github-actions github-actions bot added the stale Exempt from stale bot labeling. label Oct 29, 2025
@gen740 gen740 removed the stale Exempt from stale bot labeling. label Oct 31, 2025
@gen740
Copy link
Copy Markdown
Member Author

gen740 commented Oct 31, 2025

I changed the name of the wrapper function of warnings.warn because I think the name warn is so confusing.

Copy link
Copy Markdown
Collaborator

@kAIto47802 kAIto47802 left a comment

Choose a reason for hiding this comment

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

Thank you for the update.
I confirmed that it behaves as intended for both Python 3.12 and earlier versions, and for both the decorator and non-decorator cases.
I also confirmed that there are no remaining instances of code that should have been updated to use optuna_warn.

One minor concern is that for Python < 3.12, the user message displays optuna_warn( instead of warnings.warn(, which might be surprising for users who are accustomed to seeing warnings.warn( in standard warning messages.
However, since no information is lost compared to the original behavior, and this issue will disappear once support for versions earlier than 3.12 is dropped, it’s acceptable.

Therefore, it LGTM!

@kAIto47802 kAIto47802 removed their assignment Oct 31, 2025
Copy link
Copy Markdown
Member

@c-bata c-bata left a comment

Choose a reason for hiding this comment

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

LGTM!

@c-bata c-bata added this to the v4.7.0 milestone Nov 4, 2025
@c-bata c-bata merged commit 99fc1be into optuna:master Nov 4, 2025
13 checks passed
@gen740 gen740 deleted the warn_stacklevel branch November 7, 2025 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Change that does not break compatibility and not affect public interfaces, but improves performance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants