Skip to content

Commit 2164cb5

Browse files
committed
Produce correct value for __qualname__ and add attributes to class.
1 parent 84eff4a commit 2164cb5

5 files changed

Lines changed: 71 additions & 18 deletions

File tree

tests/snippets/class.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def square(self):
2222
assert Foo.__module__ == "class"
2323
assert Foo.square.__name__ == "square"
2424
assert Foo.square.__qualname__ == "Foo.square"
25-
25+
assert Foo.square.__module__ == "class"
2626

2727
class Bar:
2828
""" W00t """

vm/src/builtins.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::obj::objstr::{self, PyString, PyStringRef};
1818
use crate::obj::objtype::{self, PyClassRef};
1919

2020
use crate::frame::Scope;
21-
use crate::function::{Args, OptionalArg, PyFuncArgs};
21+
use crate::function::{Args, KwArgs, OptionalArg, PyFuncArgs};
2222
use crate::pyobject::{
2323
IdProtocol, ItemProtocol, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject,
2424
TypeProtocol,
@@ -783,11 +783,17 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
783783
});
784784
}
785785

786-
pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult {
787-
let function = args.shift();
788-
let name_arg = args.shift();
789-
let bases = args.args.clone();
790-
let mut metaclass = if let Some(metaclass) = args.get_optional_kwarg("metaclass") {
786+
pub fn builtin_build_class_(
787+
function: PyObjectRef,
788+
qualified_name: PyStringRef,
789+
bases: Args<PyClassRef>,
790+
mut kwargs: KwArgs,
791+
vm: &VirtualMachine,
792+
) -> PyResult {
793+
let name = qualified_name.value.split('.').next_back().unwrap();
794+
let name_obj = vm.new_str(name.to_string());
795+
796+
let mut metaclass = if let Some(metaclass) = kwargs.pop_kwarg("metaclass") {
791797
PyClassRef::try_from_object(vm, metaclass)?
792798
} else {
793799
vm.get_type()
@@ -801,21 +807,25 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu
801807
}
802808
}
803809

804-
let bases = vm.context().new_tuple(bases);
810+
let bases = bases.into_tuple(vm);
805811

806812
// Prepare uses full __getattribute__ resolution chain.
807813
let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?;
808-
let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?;
814+
let namespace = vm.invoke(prepare, vec![name_obj.clone(), bases.clone()])?;
809815

810816
let namespace: PyDictRef = TryFromObject::try_from_object(vm, namespace)?;
811817

812818
let cells = vm.ctx.new_dict();
813819

814820
vm.invoke_with_locals(function, cells.clone(), namespace.clone())?;
821+
822+
namespace.set_item("__name__", name_obj.clone(), vm)?;
823+
namespace.set_item("__qualname__", qualified_name.into_object(), vm)?;
824+
815825
let class = vm.call_method(
816826
metaclass.as_object(),
817827
"__call__",
818-
vec![name_arg, bases, namespace.into_object()],
828+
vec![name_obj, bases, namespace.into_object()],
819829
)?;
820830
cells.set_item("__class__", class.clone(), vm)?;
821831
Ok(class)

vm/src/compile.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct Compiler {
1919
nxt_label: usize,
2020
source_path: Option<String>,
2121
current_source_location: ast::Location,
22+
current_qualified_path: Option<String>,
2223
in_loop: bool,
2324
in_function_def: bool,
2425
}
@@ -75,6 +76,7 @@ impl Compiler {
7576
nxt_label: 0,
7677
source_path: None,
7778
current_source_location: ast::Location::default(),
79+
current_qualified_path: None,
7880
in_loop: false,
7981
in_function_def: false,
8082
}
@@ -610,6 +612,10 @@ impl Compiler {
610612
self.in_loop = false;
611613
self.in_function_def = true;
612614

615+
let old_qualified_path = self.current_qualified_path.clone();
616+
let qualified_name = self.create_qualified_name(name, "");
617+
self.current_qualified_path = Some(self.create_qualified_name(name, ".<locals>"));
618+
613619
self.prepare_decorators(decorator_list)?;
614620

615621
let mut flags = self.enter_function(name, args)?;
@@ -668,7 +674,7 @@ impl Compiler {
668674
});
669675
self.emit(Instruction::LoadConst {
670676
value: bytecode::Constant::String {
671-
value: name.to_string(),
677+
value: qualified_name,
672678
},
673679
});
674680

@@ -681,6 +687,7 @@ impl Compiler {
681687
name: name.to_string(),
682688
});
683689

690+
self.current_qualified_path = old_qualified_path;
684691
self.in_loop = was_in_loop;
685692
self.in_function_def = was_in_function_def;
686693
Ok(())
@@ -696,6 +703,11 @@ impl Compiler {
696703
) -> Result<(), CompileError> {
697704
let was_in_loop = self.in_loop;
698705
self.in_loop = false;
706+
707+
let old_qualified_path = self.current_qualified_path.clone();
708+
let qualified_name = self.create_qualified_name(name, "");
709+
self.current_qualified_path = Some(qualified_name.clone());
710+
699711
self.prepare_decorators(decorator_list)?;
700712
self.emit(Instruction::LoadBuildClass);
701713
let line_number = self.get_source_line_number();
@@ -711,6 +723,12 @@ impl Compiler {
711723

712724
let (new_body, doc_str) = get_doc(body);
713725

726+
self.emit(Instruction::LoadName {
727+
name: "__name__".to_string(),
728+
});
729+
self.emit(Instruction::StoreName {
730+
name: "__module__".to_string(),
731+
});
714732
self.compile_statements(new_body)?;
715733
self.emit(Instruction::LoadConst {
716734
value: bytecode::Constant::None,
@@ -737,7 +755,7 @@ impl Compiler {
737755

738756
self.emit(Instruction::LoadConst {
739757
value: bytecode::Constant::String {
740-
value: name.to_string(),
758+
value: qualified_name,
741759
},
742760
});
743761

@@ -779,6 +797,7 @@ impl Compiler {
779797
self.emit(Instruction::StoreName {
780798
name: name.to_string(),
781799
});
800+
self.current_qualified_path = old_qualified_path;
782801
self.in_loop = was_in_loop;
783802
Ok(())
784803
}
@@ -1604,6 +1623,14 @@ impl Compiler {
16041623
self.current_source_location.get_row()
16051624
}
16061625

1626+
fn create_qualified_name(&self, name: &str, suffix: &str) -> String {
1627+
if let Some(ref qualified_path) = self.current_qualified_path {
1628+
format!("{}.{}{}", qualified_path, name, suffix)
1629+
} else {
1630+
format!("{}{}", name, suffix)
1631+
}
1632+
}
1633+
16071634
fn mark_generator(&mut self) {
16081635
self.current_code_object().is_generator = true;
16091636
}

vm/src/frame.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ use crate::builtins;
1010
use crate::bytecode;
1111
use crate::function::PyFuncArgs;
1212
use crate::obj::objbool;
13-
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
1413
use crate::obj::objcode::PyCodeRef;
1514
use crate::obj::objdict::{PyDict, PyDictRef};
1615
use crate::obj::objint::PyInt;
1716
use crate::obj::objiter;
1817
use crate::obj::objlist;
1918
use crate::obj::objslice::PySlice;
2019
use crate::obj::objstr;
20+
use crate::obj::objstr::PyString;
2121
use crate::obj::objtuple::PyTuple;
2222
use crate::obj::objtype;
2323
use crate::obj::objtype::PyClassRef;
@@ -566,7 +566,10 @@ impl Frame {
566566
}
567567
}
568568
bytecode::Instruction::MakeFunction { flags } => {
569-
let qualified_name = self.pop_value();
569+
let qualified_name = self
570+
.pop_value()
571+
.downcast::<PyString>()
572+
.expect("qualified name to be a string");
570573
let code_obj = self
571574
.pop_value()
572575
.downcast()
@@ -606,7 +609,8 @@ impl Frame {
606609
.ctx
607610
.new_function(code_obj, scope, defaults, kw_only_defaults);
608611

609-
vm.set_attr(&obj, "__name__", qualified_name.clone())?;
612+
let name = qualified_name.value.split('.').next_back().unwrap();
613+
vm.set_attr(&obj, "__name__", vm.new_str(name.to_string()))?;
610614
vm.set_attr(&obj, "__qualname__", qualified_name)?;
611615
let module = self
612616
.scope
@@ -747,9 +751,7 @@ impl Frame {
747751
Ok(None)
748752
}
749753
bytecode::Instruction::LoadBuildClass => {
750-
let rustfunc =
751-
PyBuiltinFunction::new(Box::new(builtins::builtin_build_class_)).into_ref(vm);
752-
self.push_value(rustfunc.into_object());
754+
self.push_value(vm.ctx.new_rustfunc(builtins::builtin_build_class_));
753755
Ok(None)
754756
}
755757
bytecode::Instruction::UnpackSequence { size } => {

vm/src/function.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ pub trait FromArgs: Sized {
244244
/// an appropriate FromArgs implementation must be created.
245245
pub struct KwArgs<T = PyObjectRef>(HashMap<String, T>);
246246

247+
impl<T> KwArgs<T> {
248+
pub fn pop_kwarg(&mut self, name: &str) -> Option<T> {
249+
self.0.remove(name)
250+
}
251+
}
252+
247253
impl<T> FromArgs for KwArgs<T>
248254
where
249255
T: TryFromObject,
@@ -274,8 +280,16 @@ impl<T> IntoIterator for KwArgs<T> {
274280
///
275281
/// `Args` optionally accepts a generic type parameter to allow type checks
276282
/// or conversions of each argument.
283+
#[derive(Clone)]
277284
pub struct Args<T = PyObjectRef>(Vec<T>);
278285

286+
impl<T: PyValue> Args<PyRef<T>> {
287+
pub fn into_tuple(self, vm: &VirtualMachine) -> PyObjectRef {
288+
vm.ctx
289+
.new_tuple(self.0.into_iter().map(|obj| obj.into_object()).collect())
290+
}
291+
}
292+
279293
impl<T> FromArgs for Args<T>
280294
where
281295
T: TryFromObject,

0 commit comments

Comments
 (0)