Visitar URL original
Upgrade test_code and fix code bugs by youknowone · Pull Request #7440 · RustPython/RustPython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement code object __eq__/__hash__
- Add Comparable and Hashable traits for PyCode
- Compare by name, args, flags, bytecode, consts, names, vars, linetable
- Hash by tuple of key attributes matching CPython's code_hash
- Remove unused custom_ops slice in Instruction::try_from
- Add co_lnotab intentional non-implementation comment
  • Loading branch information
youknowone committed Mar 16, 2026
commit d2d2822bb9de148832f98826362ccaf00fe83d92
4 changes: 0 additions & 4 deletions crates/compiler-core/src/bytecode/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,9 @@ impl TryFrom<u8> for Instruction {
let instrumented_start = u8::from(Self::InstrumentedEndFor);
let instrumented_end = u8::from(Self::InstrumentedLine);

// No RustPython-only opcodes anymore - all opcodes match CPython 3.14
let custom_ops: &[u8] = &[];

if (cpython_start..=cpython_end).contains(&value)
|| value == resume_id
|| value == enter_executor_id
|| custom_ops.contains(&value)
|| (specialized_start..=specialized_end).contains(&value)
|| (instrumented_start..=instrumented_end).contains(&value)
{
Expand Down
77 changes: 75 additions & 2 deletions crates/vm/src/builtins/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
convert::{ToPyException, ToPyObject},
frozen,
function::OptionalArg,
types::{Constructor, Representable},
types::{Comparable, Constructor, Hashable, Representable},
};
use alloc::fmt;
use core::{
Expand Down Expand Up @@ -447,6 +447,75 @@ impl Representable for PyCode {
}
}

impl Comparable for PyCode {
fn cmp(
zelf: &Py<Self>,
other: &PyObject,
op: crate::types::PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<crate::function::PyComparisonValue> {
op.eq_only(|| {
let other = class_or_notimplemented!(Self, other);
let a = &zelf.code;
let b = &other.code;
let eq = a.obj_name == b.obj_name
&& a.arg_count == b.arg_count
&& a.posonlyarg_count == b.posonlyarg_count
&& a.kwonlyarg_count == b.kwonlyarg_count
&& a.flags == b.flags
&& a.first_line_number == b.first_line_number
&& a.instructions.original_bytes() == b.instructions.original_bytes()
&& a.linetable == b.linetable
&& a.exceptiontable == b.exceptiontable
&& a.names == b.names
&& a.varnames == b.varnames
&& a.freevars == b.freevars
&& a.cellvars == b.cellvars
&& {
let a_consts: Vec<_> = a.constants.iter().map(|c| c.0.clone()).collect();
let b_consts: Vec<_> = b.constants.iter().map(|c| c.0.clone()).collect();
if a_consts.len() != b_consts.len() {
false
} else {
let mut eq = true;
for (ac, bc) in a_consts.iter().zip(b_consts.iter()) {
if !vm.bool_eq(ac, bc)? {
eq = false;
break;
}
}
eq
}
};
Ok(eq.into())
})
}
}

impl Hashable for PyCode {
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<crate::common::hash::PyHash> {
let code = &zelf.code;
// Hash a tuple of key attributes, matching CPython's code_hash
let tuple = vm.ctx.new_tuple(vec![
vm.ctx.new_str(code.obj_name.as_str()).into(),
vm.ctx.new_int(code.arg_count).into(),
vm.ctx.new_int(code.posonlyarg_count).into(),
vm.ctx.new_int(code.kwonlyarg_count).into(),
vm.ctx.new_int(code.varnames.len()).into(),
vm.ctx.new_int(code.flags.bits()).into(),
vm.ctx
.new_int(code.first_line_number.map_or(0, |n| n.get()) as i64)
.into(),
vm.ctx.new_bytes(code.instructions.original_bytes()).into(),
{
let consts: Vec<_> = code.constants.iter().map(|c| c.0.clone()).collect();
vm.ctx.new_tuple(consts).into()
},
]);
tuple.as_object().hash(vm)
}
}
Comment on lines +450 to +517

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

PyCode equality currently ignores meaningful state fields.

cmp can return True even when source_path, qualname, or max_stackdepth differ. That makes distinct code objects compare equal after replace(...) changes to those fields.

Suggested fix
 impl Comparable for PyCode {
@@
-            let eq = a.obj_name == b.obj_name
+            let eq = a.obj_name == b.obj_name
+                && a.source_path == b.source_path
+                && a.qualname == b.qualname
                 && a.arg_count == b.arg_count
                 && a.posonlyarg_count == b.posonlyarg_count
                 && a.kwonlyarg_count == b.kwonlyarg_count
+                && a.max_stackdepth == b.max_stackdepth
                 && a.flags == b.flags
                 && a.first_line_number == b.first_line_number
                 && a.instructions.original_bytes() == b.instructions.original_bytes()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/code.rs` around lines 450 - 517, The PyCode equality
and hashing omit meaningful fields (source_path, qualname, max_stackdepth), so
update Comparable::cmp (the impl for PyCode::cmp) to also compare
code.source_path, code.qualname, and code.max_stackdepth alongside the existing
checks (add these three comparisons in the big `eq = ...` expression and include
them inside the constants/iter section as appropriate), and update
Hashable::hash (the impl for PyCode::hash) to include code.source_path,
code.qualname, and code.max_stackdepth in the tuple used for hashing (convert
Option/source and qualname to the appropriate VM objects like strings/None and
include max_stackdepth as an int) so that object equality and hashing remain
consistent.


// Arguments for code object constructor
#[derive(FromArgs)]
pub struct PyCodeNewArgs {
Expand Down Expand Up @@ -595,7 +664,7 @@ impl Constructor for PyCode {
}
}

#[pyclass(with(Representable, Constructor), flags(HAS_WEAKREF))]
#[pyclass(with(Representable, Constructor, Comparable, Hashable), flags(HAS_WEAKREF))]
impl PyCode {
#[pygetset]
const fn co_posonlyargcount(&self) -> usize {
Expand Down Expand Up @@ -721,6 +790,10 @@ impl PyCode {
vm.ctx.new_bytes(self.code.exceptiontable.to_vec())
}

// co_lnotab is intentionally not implemented.
// It was deprecated since 3.12 and scheduled for removal in 3.14.
// Use co_lines() or co_linetable instead.
Comment on lines +796 to +799

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Python co_lnotab deprecation removal version

💡 Result:

  • code.co_lnotab was deprecated in Python 3.10 by PEP 626 (use code.co_lines() instead). [1]
  • It started emitting a proper DeprecationWarning in Python 3.12. [1]
  • Although it was initially tracked as “scheduled for removal in Python 3.14” in early 3.12-era notes, that removal was postponed, and the current plan is removal in Python 3.15. [2]

So: expect co_lnotab to be removed in Python 3.15 (not 3.14). [2]

Sources: [1] (Python 3.12 “What’s New”), [2] (python.org core-dev discussion referencing the postponement PR).


Update comment with accurate deprecation timeline.

The comment states co_lnotab was "deprecated since 3.12 and scheduled for removal in 3.14." However, co_lnotab was actually deprecated in Python 3.10 (per PEP 626), with the DeprecationWarning added in Python 3.12. The scheduled removal date was also updated from Python 3.14 to Python 3.15. Consider updating the comment to reflect: "deprecated in Python 3.10, scheduled for removal in Python 3.15."

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

In `@crates/vm/src/builtins/code.rs` around lines 796 - 799, Update the inline
comment about co_lnotab to reflect the correct timeline: change the text that
currently reads "deprecated since 3.12 and scheduled for removal in 3.14" to
state that co_lnotab was deprecated in Python 3.10 (per PEP 626), had
DeprecationWarning added in Python 3.12, and is scheduled for removal in Python
3.15; adjust the surrounding sentence to still recommend using co_lines() or
co_linetable and reference co_lnotab, co_lines, and co_linetable so the intent
remains clear.


#[pymethod]
pub fn co_lines(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
// TODO: Implement lazy iterator (lineiterator) like CPython for better performance
Expand Down