Skip to content

Refactor warn.rs and _warnings module#7023

Merged
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:cleanup-warnings
Feb 6, 2026
Merged

Refactor warn.rs and _warnings module#7023
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:cleanup-warnings

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Feb 6, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Stop emitting redundant encoding warnings during stdin/stdout/stderr initialization; warnings now appear only via the explicit public encoding API.
  • Refactor

    • Reworked the warnings system for more reliable filter matching, registry handling, and frame context resolution to improve warning delivery and tracking.
    • Public warning API updated to a more structured flow (may affect integrations relying on previous behavior).
  • New Features

    • Added two additional common names as public constants.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Removes an internal EncodingWarning emission and refactors the warnings subsystem: warn() now returns unit and delegates through new helpers (registry/filter/module normalization and context setup); adds two interned constant names to the VM context. No exported Python API signatures changed beyond warn semantics.

Changes

Cohort / File(s) Summary
Encoding handling
crates/vm/src/stdlib/io.rs
Suppresses the internal EncodingWarning in TextIOWrapper::resolve_encoding; warning remains on the public io.text_encoding() path.
Warnings core & helpers
crates/vm/src/warn.rs
Major refactor: warn now returns PyResult<()>; introduces warn_with_skip, update_registry, normalize_module, get_filter, expanded setup_context, revised frame-walking, and stricter type checks for defaultaction/onceregistry.
Stdlib warnings binding
crates/vm/src/stdlib/warnings.rs
warn now delegates to the central crate-level warn function using a PyObject message; decorator attributes simplified (changed to generic #[pyattr]/#[pyfunction]) and several internal names exposed with underscored identifiers.
VM context constants
crates/vm/src/vm/context.rs
Adds two new interned ConstName fields: defaultaction and onceregistry, and initializes them in ConstName::new.

Sequence Diagram(s)

sequenceDiagram
    participant PyCode as Python caller
    participant VMWarn as vm::warn / warn_with_skip
    participant WState as sys.modules["warnings"] / registry
    participant Show as show_warning (hook / stderr)

    PyCode->>VMWarn: warn(message, category, stack_level, ...)
    VMWarn->>VMWarn: setup_context(stack_level, skip_prefixes)
    VMWarn->>WState: get_warnings_filters(), get_once_registry(), get_default_action()
    VMWarn->>VMWarn: get_filter(category, text, lineno, module)
    alt filter is "ignore"
        VMWarn->>WState: update_registry(key)
        VMWarn-->>PyCode: return
    else filter is "error"/"always"/"default"/"module"/"once"
        VMWarn->>WState: update_registry(key) (as needed)
        VMWarn->>Show: call_show_warning(WarningMessage)
        Show-->>VMWarn: handled
        VMWarn-->>PyCode: return
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • fanninpm

Poem

🐰 I hopped through code and cleared the noise,
Filters sorted, registries with poise,
Encoding hushed where internal lights dim,
Warnings routed, tidy and prim,
A carrot cheers this tidy change! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: refactoring of warn.rs and the _warnings module with simplified logic and public API adjustments.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@youknowone youknowone marked this pull request as ready for review February 6, 2026 08:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/vm/src/warn.rs (1)

553-611: ⚠️ Potential issue | 🟡 Minor

Deduplicate the stack-walk loops.
The two branches differ only by the frame-advance function; extract that difference and run a single loop to avoid duplication. As per coding guidelines, When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code.

🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/warnings.rs`:
- Around line 11-17: The call to crate::warn::warn passes stack_level as
(stack_level as isize) which can wrap for very large usize values; change this
by performing a safe conversion from usize to isize (e.g., use isize::try_from
or usize::try_into) and handle the failure path explicitly (either clamp to
isize::MAX or return an error/early-return) before calling crate::warn::warn;
update the call site that constructs the args (the stack_level conversion in the
warn invocation and any surrounding logic that sets stack_level) so it uses the
checked conversion result and passes the resulting isize safely to
crate::warn::warn.

In `@crates/vm/src/warn.rs`:
- Around line 249-256: In normalize_module, the code uses filename.char_len() to
compute a slice index into filename.as_wtf8(), which mixes character count with
byte slicing and breaks for non-ASCII names; change to use filename.byte_len()
(or similar byte-length API) and use that byte length when computing the slice
(e.g., replace the char_len() binding with a byte_len variable and slice
as_wtf8()[..byte_len - 3]) so the ".py" strip uses byte offsets consistent with
as_wtf8(); keep the existing ends_with(b".py") check and only adjust the length
source in normalize_module.

@youknowone youknowone changed the title impl more warnings Refactor warn.rs and _warnings module Feb 6, 2026
- Add already_warned() with filter version tracking
- Add type validation for _defaultaction and _onceregistry
- Use direct function names for _warnings pyattr/pyfunction
- Route stdlib::warnings::warn through warn::warn
- Remove spurious EncodingWarning from TextIOWrapper
- Clean up comments and simplify check_matched error handling
- Remove expectedFailure from 3 passing test_warnings tests
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/vm/src/warn.rs`:
- Around line 288-292: The code currently silences conversion errors by using
tmp_item[4].try_int(vm).map_or(0, ...) to set ln, which masks malformed filter
configs; change this to propagate the integer conversion error instead of
defaulting to 0 (e.g., use tmp_item[4].try_int(vm)? and then convert to
usize/isize as appropriate, or map_err to return the VM error), so that failures
in tmp_item[4].try_int(vm) bubble up rather than making ln == 0; update any
downstream handling of ln (used in the if condition with is_subclass,
check_matched, lineno) to expect a fallible conversion.
🧹 Nitpick comments (1)
crates/vm/src/warn.rs (1)

497-515: writeln! result is silently discarded.

The writeln! return value on line 506 is not handled. While this is acceptable for a best-effort warning display (matching CPython's behavior of ignoring stderr write failures), it will produce a compiler warning unless explicitly suppressed.

Proposed fix
-    writeln!(
+    let _ = writeln!(
         stderr,
         "{}:{}: {}: {}",
         filename,
         lineno,
         category.name(),
         text
     );

Comment on lines +288 to +292
let is_subclass = category.is_subclass(&tmp_item[2], vm)?;
let good_mod = check_matched(&tmp_item[3], &module, vm)?;
let ln: usize = tmp_item[4].try_int(vm).map_or(0, |v| v.as_u32_mask() as _);

if good_msg && good_mod && is_subclass && (ln == 0 || lineno == ln) {
if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n crates/vm/src/warn.rs | sed -n '280,300p'

Repository: RustPython/RustPython

Length of output: 934


🌐 Web query:

CPython _warnings.c get_filter function lineno error handling

💡 Result:

In current CPython main, get_filter() itself doesn’t “parse” lineno; it receives lineno as a Py_ssize_t argument and delegates filter matching to filter_search(). (chromium.googlesource.com)

The lineno error handling you’re asking about is for the filter entry’s stored line number (ln_obj, i.e., the 5th element of each filter tuple). In filter_search() CPython does:

  • ln = PyLong_AsSsize_t(ln_obj);
  • If conversion fails (e.g., ln_obj is not an int-like object, or it overflows Py_ssize_t), PyLong_AsSsize_t() sets an exception, and the code detects that with if (ln == -1 && PyErr_Occurred()), then:
    • decref’s the temporary tuple item
    • returns false, which propagates out so get_filter() returns NULL (error). (chromium.googlesource.com)

So, the behavior is: invalid/overflowing ln_obj → exception from PyLong_AsSsize_t is propagated unchanged; there’s no custom warning-filter-specific error message for “bad lineno”, just the generic conversion error. (chromium.googlesource.com)

Citations:


Silently defaulting ln to 0 on try_int failure masks malformed filter configurations.

Line 290: when tmp_item[4] fails integer conversion, map_or(0, ...) silently treats it as "match any line number." CPython's filter_search() in _warnings.c calls PyLong_AsSsize_t() which propagates conversion errors instead. This behavioral difference could hide filter configuration bugs.

Proposed fix
-        let ln: usize = tmp_item[4].try_int(vm).map_or(0, |v| v.as_u32_mask() as _);
+        let ln: usize = tmp_item[4].try_int(vm).map(|v| v.as_u32_mask() as usize)?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let is_subclass = category.is_subclass(&tmp_item[2], vm)?;
let good_mod = check_matched(&tmp_item[3], &module, vm)?;
let ln: usize = tmp_item[4].try_int(vm).map_or(0, |v| v.as_u32_mask() as _);
if good_msg && good_mod && is_subclass && (ln == 0 || lineno == ln) {
if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) {
let is_subclass = category.is_subclass(&tmp_item[2], vm)?;
let good_mod = check_matched(&tmp_item[3], &module, vm)?;
let ln: usize = tmp_item[4].try_int(vm).map(|v| v.as_u32_mask() as usize)?;
if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) {
🤖 Prompt for AI Agents
In `@crates/vm/src/warn.rs` around lines 288 - 292, The code currently silences
conversion errors by using tmp_item[4].try_int(vm).map_or(0, ...) to set ln,
which masks malformed filter configs; change this to propagate the integer
conversion error instead of defaulting to 0 (e.g., use tmp_item[4].try_int(vm)?
and then convert to usize/isize as appropriate, or map_err to return the VM
error), so that failures in tmp_item[4].try_int(vm) bubble up rather than making
ln == 0; update any downstream handling of ln (used in the if condition with
is_subclass, check_matched, lineno) to expect a fallible conversion.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] lib: cpython/Lib/warnings.py
[x] lib: cpython/Lib/_py_warnings.py
[ ] test: cpython/Lib/test/test_warnings (TODO: 14)

dependencies:

  • warnings

dependent tests: (48 tests)

  • warnings: test_argparse test_asyncio test_buffer test_builtin test_codecs test_codeop test_coroutines test_ctypes test_decimal test_descr test_fnmatch test_fstring test_genericpath test_glob test_global test_grammar test_gzip test_hashlib test_hmac test_importlib test_inspect test_io test_itertools test_logging test_ntpath test_os test_pickle test_posix test_pty test_pyclbr test_random test_re test_runpy test_set test_socket test_sqlite3 test_str test_string_literals test_support test_sys test_tempfile test_threading test_typing test_unicode_file_functions test_unittest test_unparse test_xml_etree test_zipimport

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@youknowone youknowone merged commit 234bdda into RustPython:main Feb 6, 2026
14 checks passed
@youknowone youknowone deleted the cleanup-warnings branch February 6, 2026 11:46
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.

1 participant