Remove unused rust impl for formatting dis output by ShaharNaveh · Pull Request #7660 · RustPython/RustPython · GitHub
Skip to content

Remove unused rust impl for formatting dis output#7660

Open
ShaharNaveh wants to merge 11 commits intoRustPython:mainfrom
ShaharNaveh:remove-fmt-dis
Open

Remove unused rust impl for formatting dis output#7660
ShaharNaveh wants to merge 11 commits intoRustPython:mainfrom
ShaharNaveh:remove-fmt-dis

Conversation

@ShaharNaveh
Copy link
Copy Markdown
Contributor

@ShaharNaveh ShaharNaveh commented Apr 23, 2026

This was used back when we had a hacky _dis module (that was removed at #6690).
I guess we kept maintaining the fmt_dis method as it was still technicaly used as we would implement Display for PyCode, but since we no longer actually use it anywhere it's dead code.

Display of PyCode was called here:

Close #7433

Summary by CodeRabbit

  • Removed Features

    • Bytecode disassembly/display formatting and the disassembly example program have been removed.
  • Tests

    • Several snapshot-based tests were removed.
    • New parameterized unit tests added to validate disassembly output across various language constructs.
  • Chores

    • Workspace test dependencies updated (insta bumped) and test-case introduced for workspace testing.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Remove disassembly/display formatting and the dis example; relocate/restore snapshot tests into stdlib with workspace test tooling; tweak a debug print in IR and delete several compile-time snapshot tests. No runtime bytecode or VM execution logic changed.

Changes

Cohort / File(s) Summary
Workspace deps
Cargo.toml
Update workspace deps: replace insta = "1.46" with insta = "1.47.2"; add test-case = "3.3.1".
Stdlib dev-deps wiring
crates/stdlib/Cargo.toml
Add [dev-dependencies] section referencing workspace insta and test-case (workspace = true).
Move/add snapshot tests
crates/stdlib/src/_opcode.rs
Add #[cfg(test)] test module with parameterized #[test_case] tests that compile Python snippets, run dis, and assert snapshots via insta.
Remove snapshot tests
crates/codegen/src/compile.rs
Delete snapshot-test helper macro and multiple unit tests that asserted disassembly outputs.
IR debug print tweak
crates/codegen/src/ir.rs
Change debug tracing to print display_arg with :? instead of using instr.display(...).
Remove disassembly display API
crates/compiler-core/src/bytecode.rs, crates/compiler-core/src/bytecode/instruction.rs
Remove disassembly/formatting routines and trait methods (e.g., display_inner, display_expand_code_objects, impl fmt::Display for CodeObject, InstructionMetadata::fmt_dis, per-instruction fmt_dis and related dispatch).
Remove PyCode Display impl
crates/vm/src/builtins/code.rs
Remove impl fmt::Display for PyCode; PyCode no longer implements Display.
Example removal
examples/dis.rs
Delete dis example program that parsed flags, compiled files, and printed disassembly.
Codegen dev-deps change
crates/codegen/Cargo.toml
Remove insta from dev-dependencies of the rustpython-codegen crate.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • coolreader18

Poem

🐰 I nibbled at prints and tucked them tight,

Moved snapshots to a snug, new light.
Disassembly maps folded, neat and small,
Tests now hop in stdlib's hall. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main objective: removing the unused Rust implementation for formatting disassembly output (fmt_dis and related Display impls).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@ShaharNaveh
Copy link
Copy Markdown
Contributor Author

ShaharNaveh commented Apr 23, 2026

@youknowone seems that the places where we are using our implementation of dis.dis is at:

What do you think will be the best approach with solving this?

@youknowone
Copy link
Copy Markdown
Member

lets remove them if obsolete

@fanninpm
Copy link
Copy Markdown
Contributor

Remove unused dep

🎉

@ShaharNaveh ShaharNaveh marked this pull request as ready for review April 23, 2026 20:51
Copy link
Copy Markdown
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.

Caution

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

⚠️ Outside diff range comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)

1371-1397: ⚠️ Potential issue | 🔴 Critical

frame.rs still calls the now-removed InstructionMetadata::display method — this will fail to compile.

The trait definition at lines 1371–1397 no longer includes the display method, but crates/vm/src/frame.rs has two call sites:

  • Line 2101: instruction.display(arg, &self.code.code).to_string() in the flame_guard! macro
  • Line 2115: instruction.display(arg, &self.code.code) in the feature-gated trace output

Both must be updated (e.g., to {:?} formatting like ir.rs) before this change can compile.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/compiler-core/src/bytecode/instruction.rs` around lines 1371 - 1397,
InstructionMetadata no longer provides display, so update the two call sites in
frame.rs that call instruction.display(arg, &self.code.code): inside the
flame_guard! usage (previously instruction.display(...).to_string()) and in the
feature-gated trace output (previously instruction.display(...)). Replace those
calls with Debug-style formatting (e.g. format!("{:?}", instruction) or using
the Debug formatter) so they compile against the current InstructionMetadata
trait; ensure the flame_guard! site produces a String (format! already does) and
the trace output uses the formatted Debug value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 1371-1397: InstructionMetadata no longer provides display, so
update the two call sites in frame.rs that call instruction.display(arg,
&self.code.code): inside the flame_guard! usage (previously
instruction.display(...).to_string()) and in the feature-gated trace output
(previously instruction.display(...)). Replace those calls with Debug-style
formatting (e.g. format!("{:?}", instruction) or using the Debug formatter) so
they compile against the current InstructionMetadata trait; ensure the
flame_guard! site produces a String (format! already does) and the trace output
uses the formatted Debug value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: ab1a606d-72fe-43ac-97bf-03acadd96dc4

📥 Commits

Reviewing files that changed from the base of the PR and between 5081f76 and fb1e637.

⛔ Files ignored due to path filters (12)
  • Cargo.lock is excluded by !**/*.lock
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__bare_function_annotations_check_attribute_and_subscript_expressions.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__const_bool_not_op.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__constant_true_if_pass_keeps_line_anchor_nop.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • Cargo.toml
  • crates/codegen/Cargo.toml
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode.rs
  • crates/compiler-core/src/bytecode/instruction.rs
  • crates/vm/src/builtins/code.rs
  • examples/dis.rs
💤 Files with no reviewable changes (6)
  • crates/codegen/Cargo.toml
  • crates/vm/src/builtins/code.rs
  • Cargo.toml
  • examples/dis.rs
  • crates/compiler-core/src/bytecode.rs
  • crates/codegen/src/compile.rs

Comment on lines -11634 to -11638
fn test_if_ors() {
assert_dis_snapshot!(compile_exec(
"\
if True or False or False:
pass
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oh.. are these test going to be entirely deleted? This is not desired. Can we replace the snapshot test to other way not depending on internal dis?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The only way I can think of is to move them under crates/vm or crates/stdlib, then parsing the source code into a PyCode, then run it like:

let module = vm.import("dis", 0)?;
let dis = module.get_attr("dis", vm)?;
let res = dis.call(my_pycode, vm)?;

// <convert res to String and pass to Insta>

Copy link
Copy Markdown
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

🧹 Nitpick comments (3)
crates/stdlib/src/_opcode.rs (3)

309-331: Drop the unused Result<(), ()> return type.

The ? on Line 324 operates inside the interp.enter closure (which returns PyResult<String>), and any Err is already converted to a panic by .unwrap() on Line 326. The outer function always returns Ok(()), so Result<(), ()> and the final Ok(()) are dead surface area.

Proposed simplification
-    fn dis_output(source: &str) -> Result<(), ()> {
+    fn dis_output(source: &str) {
         let builder = vm::Interpreter::builder(Default::default());
         let stdlib_defs = crate::stdlib_module_defs(&builder.ctx);
         let interp = builder.add_native_modules(&stdlib_defs).build();
 
         let output = interp
             .enter(|vm| {
                 let pycode = vm
-                    .compile(&source, Mode::Exec, "?".into())
-                    .map_err(|err| vm.new_syntax_error(&err, Some(&source)))
+                    .compile(source, Mode::Exec, "?".into())
+                    .map_err(|err| vm.new_syntax_error(&err, Some(source)))
                     .unwrap();
 
                 let dis_module = vm.import("dis", 0).unwrap();
                 let dis = dis_module.get_attr("dis", vm).unwrap();
 
                 dis.call((pycode,), vm)?.try_into_value::<String>(vm)
             })
             .unwrap();
 
         insta::assert_snapshot!(insta::internals::AutoName, output, source);
-
-        Ok(())
     }

(Also drops the unnecessary &source reborrow since source is already &str.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/stdlib/src/_opcode.rs` around lines 309 - 331, The function dis_output
currently returns Result<(), ()> with a final Ok(()) and reborrows source;
simplify it by changing the signature of dis_output to return () (unit), remove
the final Ok(()) return, and stop reborrowing source when calling vm.compile
(use source instead of &source); keep the existing interp.enter closure and its
internal error handling as-is (the closure still returns PyResult<String> and
unwraps).

310-312: Avoid rebuilding a fresh Interpreter per test case.

The dis_output function has 8 #[test_case] invocations, each spinning up a new Interpreter, initializing the stdlib, and re-importing dis. This multiplies test runtime and can noticeably slow cargo test. Consider sharing an interpreter across cases via OnceLock/LazyLock:

Sketch
use std::sync::OnceLock;

fn shared_interp() -> &'static vm::Interpreter {
    static INTERP: OnceLock<vm::Interpreter> = OnceLock::new();
    INTERP.get_or_init(|| {
        let builder = vm::Interpreter::builder(Default::default());
        let stdlib_defs = crate::stdlib_module_defs(&builder.ctx);
        builder.add_native_modules(&stdlib_defs).build()
    })
}

Then call shared_interp().enter(|vm| { … }) from dis_output. Since VirtualMachine contains RefCell and UnsafeCell, verify thread-safety for parallel test execution; if not Sync, either use serial_test or pin tests to single-threaded execution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/stdlib/src/_opcode.rs` around lines 310 - 312, The tests rebuild an
Interpreter for every dis_output #[test_case], slowing test runs; create and
reuse a shared static interpreter (e.g., a shared_interp function backed by
std::sync::OnceLock or LazyLock that initializes vm::Interpreter via
vm::Interpreter::builder and crate::stdlib_module_defs once) and call
shared_interp().enter(|vm| { … }) inside dis_output instead of rebuilding;
verify whether the VM/VirtualMachine types are Sync/Send—if not, either run
those tests serially (use serial_test or #[test] with single-threaded execution)
or restrict the static to single-threaded use to avoid data races.

328-328: Remove unstable insta::internals::AutoName and use simplified public API.

The internals module is not part of insta's stable surface. With test_case descriptions driving the snapshot filename suffix, auto-naming works correctly using the simplified syntax:

-        insta::assert_snapshot!(insta::internals::AutoName, output, source);
+        insta::assert_snapshot!(output, source);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/stdlib/src/_opcode.rs` at line 328, Replace the unstable internals
usage in the snapshot assertion: remove the first argument
`insta::internals::AutoName` from the `insta::assert_snapshot!` invocation so it
uses the public simplified API (i.e., call `insta::assert_snapshot!(output,
source)`), leaving the auto-naming behavior driven by the test_case description
intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/stdlib/src/_opcode.rs`:
- Around line 314-326: The disassembly capture currently assumes dis.dis()
returns a string and panics when it returns None; update the dis_output function
so inside interp.enter (the closure passed to interp.enter) you compile the
source as before, create an io.StringIO via vm.import("io") and
io.get_attr("StringIO"), replace sys.stdout by setting sys.stdout to that
StringIO (save the previous stdout), call dis = dis_module.get_attr("dis") and
invoke dis.call((pycode,), vm) checking the call result, restore sys.stdout to
the saved value, then read the captured text via
string_io.call_method("getvalue", (), vm)?.try_into_value::<String>(vm) and
return that String (propagate errors with ?); also simplify the dis_output
signature to fn dis_output(source: &str) -> String (or -> PyResult<String>
inside enter) instead of Result<(), ()>, and remove reliance on
insta::internals::AutoName by switching to the public insta naming API.

---

Nitpick comments:
In `@crates/stdlib/src/_opcode.rs`:
- Around line 309-331: The function dis_output currently returns Result<(), ()>
with a final Ok(()) and reborrows source; simplify it by changing the signature
of dis_output to return () (unit), remove the final Ok(()) return, and stop
reborrowing source when calling vm.compile (use source instead of &source); keep
the existing interp.enter closure and its internal error handling as-is (the
closure still returns PyResult<String> and unwraps).
- Around line 310-312: The tests rebuild an Interpreter for every dis_output
#[test_case], slowing test runs; create and reuse a shared static interpreter
(e.g., a shared_interp function backed by std::sync::OnceLock or LazyLock that
initializes vm::Interpreter via vm::Interpreter::builder and
crate::stdlib_module_defs once) and call shared_interp().enter(|vm| { … })
inside dis_output instead of rebuilding; verify whether the VM/VirtualMachine
types are Sync/Send—if not, either run those tests serially (use serial_test or
#[test] with single-threaded execution) or restrict the static to
single-threaded use to avoid data races.
- Line 328: Replace the unstable internals usage in the snapshot assertion:
remove the first argument `insta::internals::AutoName` from the
`insta::assert_snapshot!` invocation so it uses the public simplified API (i.e.,
call `insta::assert_snapshot!(output, source)`), leaving the auto-naming
behavior driven by the test_case description intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 5a7fe94b-c04e-4c9e-abc7-7361c0809423

📥 Commits

Reviewing files that changed from the base of the PR and between fb1e637 and b5b99d6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/stdlib/Cargo.toml
  • crates/stdlib/src/_opcode.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml

Comment thread crates/stdlib/src/_opcode.rs Outdated
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
crates/stdlib/src/_opcode.rs (1)

314-328: ⚠️ Potential issue | 🔴 Critical

Runtime panic: dis.dis() returns None, not a string — all test cases will fail.

dis.dis(code) (like CPython) prints to stdout and returns None. The try_into_value::<String>(vm) on Line 324 will therefore error, and the subsequent .unwrap() on Line 326 will panic for every parametrized case.

To capture the disassembly, redirect sys.stdout to an io.StringIO before the call and read getvalue() afterwards, or invoke a helper that returns a string (e.g., build the output via dis.Bytecode(code).dis() or similar).

Additionally, insta::internals::AutoName on Line 328 is a non-public API; prefer the public naming surface (e.g., passing the name explicitly or using assert_snapshot! without an explicit name so insta derives it from the function/test-case).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/stdlib/src/_opcode.rs` around lines 314 - 328, The test currently
assumes dis.dis(code) returns a string and calls try_into_value::<String>(vm),
which panics because dis.dis prints to stdout and returns None; modify the
closure passed to interp.enter to capture the disassembly by redirecting
sys.stdout to an io.StringIO (or call a helper like dis.Bytecode(code).dis()
that returns a string), then read string_io.getvalue() and return that String
instead of trying to convert None; also replace the non-public
insta::internals::AutoName usage in the insta::assert_snapshot! invocation with
the public API (omit the explicit internal name so insta derives it or pass an
explicit name) so snapshots use supported naming.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/stdlib/src/_opcode.rs`:
- Around line 314-328: The test currently assumes dis.dis(code) returns a string
and calls try_into_value::<String>(vm), which panics because dis.dis prints to
stdout and returns None; modify the closure passed to interp.enter to capture
the disassembly by redirecting sys.stdout to an io.StringIO (or call a helper
like dis.Bytecode(code).dis() that returns a string), then read
string_io.getvalue() and return that String instead of trying to convert None;
also replace the non-public insta::internals::AutoName usage in the
insta::assert_snapshot! invocation with the public API (omit the explicit
internal name so insta derives it or pass an explicit name) so snapshots use
supported naming.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: fd1a3b81-c62f-4a02-83dc-cf3537731258

📥 Commits

Reviewing files that changed from the base of the PR and between 8347d97 and 9b29edb.

📒 Files selected for processing (1)
  • crates/stdlib/src/_opcode.rs

Comment thread crates/stdlib/src/_opcode.rs Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It seems like no matter what I do I can't import "dis". I'd appreciate some help on this.
@fanninpm @youknowone

@ShaharNaveh ShaharNaveh requested a review from youknowone April 26, 2026 14:20
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.

3 participants