Skip to content

Commit 455d3f6

Browse files
committed
Fix qualname
1 parent 81ea25f commit 455d3f6

File tree

7 files changed

+88
-21
lines changed

7 files changed

+88
-21
lines changed

Lib/test/test_asyncgen.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,6 @@ async def async_gen_wrapper():
375375

376376
self.compare_generators(sync_gen_wrapper(), async_gen_wrapper())
377377

378-
# TODO: RUSTPYTHON
379-
@unittest.expectedFailure
380378
def test_async_gen_api_01(self):
381379
async def gen():
382380
yield 123
@@ -517,8 +515,6 @@ async def consume():
517515
res = self.loop.run_until_complete(consume())
518516
self.assertEqual(res, [1, 2])
519517

520-
# TODO: RUSTPYTHON, NameError: name 'aiter' is not defined
521-
@unittest.expectedFailure
522518
def test_async_gen_aiter_class(self):
523519
results = []
524520
class Gen:

crates/vm/src/builtins/asyncgenerator.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ impl PyAsyncGen {
3333
&self.inner
3434
}
3535

36-
pub fn new(frame: FrameRef, name: PyStrRef) -> Self {
36+
pub fn new(frame: FrameRef, name: PyStrRef, qualname: PyStrRef) -> Self {
3737
Self {
38-
inner: Coro::new(frame, name),
38+
inner: Coro::new(frame, name, qualname),
3939
running_async: AtomicCell::new(false),
4040
}
4141
}
@@ -50,6 +50,16 @@ impl PyAsyncGen {
5050
self.inner.set_name(name)
5151
}
5252

53+
#[pygetset]
54+
fn __qualname__(&self) -> PyStrRef {
55+
self.inner.qualname()
56+
}
57+
58+
#[pygetset(setter)]
59+
fn set___qualname__(&self, qualname: PyStrRef) {
60+
self.inner.set_qualname(qualname)
61+
}
62+
5363
#[pygetset]
5464
fn ag_await(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
5565
self.inner.frame().yield_from_target()

crates/vm/src/builtins/coroutine.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ impl PyCoroutine {
2929
&self.inner
3030
}
3131

32-
pub fn new(frame: FrameRef, name: PyStrRef) -> Self {
32+
pub fn new(frame: FrameRef, name: PyStrRef, qualname: PyStrRef) -> Self {
3333
Self {
34-
inner: Coro::new(frame, name),
34+
inner: Coro::new(frame, name, qualname),
3535
}
3636
}
3737

@@ -45,6 +45,16 @@ impl PyCoroutine {
4545
self.inner.set_name(name)
4646
}
4747

48+
#[pygetset]
49+
fn __qualname__(&self) -> PyStrRef {
50+
self.inner.qualname()
51+
}
52+
53+
#[pygetset(setter)]
54+
fn set___qualname__(&self, qualname: PyStrRef) {
55+
self.inner.set_qualname(qualname)
56+
}
57+
4858
#[pymethod(name = "__await__")]
4959
const fn r#await(zelf: PyRef<Self>) -> PyCoroutineWrapper {
5060
PyCoroutineWrapper { coro: zelf }

crates/vm/src/builtins/function.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,15 @@ impl Py<PyFunction> {
425425
let is_gen = code.flags.contains(bytecode::CodeFlags::IS_GENERATOR);
426426
let is_coro = code.flags.contains(bytecode::CodeFlags::IS_COROUTINE);
427427
match (is_gen, is_coro) {
428-
(true, false) => Ok(PyGenerator::new(frame, self.__name__()).into_pyobject(vm)),
429-
(false, true) => Ok(PyCoroutine::new(frame, self.__name__()).into_pyobject(vm)),
430-
(true, true) => Ok(PyAsyncGen::new(frame, self.__name__()).into_pyobject(vm)),
428+
(true, false) => {
429+
Ok(PyGenerator::new(frame, self.__name__(), self.__qualname__()).into_pyobject(vm))
430+
}
431+
(false, true) => {
432+
Ok(PyCoroutine::new(frame, self.__name__(), self.__qualname__()).into_pyobject(vm))
433+
}
434+
(true, true) => {
435+
Ok(PyAsyncGen::new(frame, self.__name__(), self.__qualname__()).into_pyobject(vm))
436+
}
431437
(false, false) => vm.run_frame(frame),
432438
}
433439
}

crates/vm/src/builtins/generator.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ impl PyGenerator {
3232
&self.inner
3333
}
3434

35-
pub fn new(frame: FrameRef, name: PyStrRef) -> Self {
35+
pub fn new(frame: FrameRef, name: PyStrRef, qualname: PyStrRef) -> Self {
3636
Self {
37-
inner: Coro::new(frame, name),
37+
inner: Coro::new(frame, name, qualname),
3838
}
3939
}
4040

@@ -48,6 +48,16 @@ impl PyGenerator {
4848
self.inner.set_name(name)
4949
}
5050

51+
#[pygetset]
52+
fn __qualname__(&self) -> PyStrRef {
53+
self.inner.qualname()
54+
}
55+
56+
#[pygetset(setter)]
57+
fn set___qualname__(&self, qualname: PyStrRef) {
58+
self.inner.set_qualname(qualname)
59+
}
60+
5161
#[pygetset]
5262
fn gi_frame(&self, _vm: &VirtualMachine) -> FrameRef {
5363
self.inner.frame()

crates/vm/src/coroutine.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct Coro {
3232
// code
3333
// _weakreflist
3434
name: PyMutex<PyStrRef>,
35-
// qualname
35+
qualname: PyMutex<PyStrRef>,
3636
exception: PyMutex<Option<PyBaseExceptionRef>>, // exc_state
3737
}
3838

@@ -48,13 +48,14 @@ fn gen_name(jen: &PyObject, vm: &VirtualMachine) -> &'static str {
4848
}
4949

5050
impl Coro {
51-
pub fn new(frame: FrameRef, name: PyStrRef) -> Self {
51+
pub fn new(frame: FrameRef, name: PyStrRef, qualname: PyStrRef) -> Self {
5252
Self {
5353
frame,
5454
closed: AtomicCell::new(false),
5555
running: AtomicCell::new(false),
5656
exception: PyMutex::default(),
5757
name: PyMutex::new(name),
58+
qualname: PyMutex::new(qualname),
5859
}
5960
}
6061

@@ -188,6 +189,14 @@ impl Coro {
188189
*self.name.lock() = name;
189190
}
190191

192+
pub fn qualname(&self) -> PyStrRef {
193+
self.qualname.lock().clone()
194+
}
195+
196+
pub fn set_qualname(&self, qualname: PyStrRef) {
197+
*self.qualname.lock() = qualname;
198+
}
199+
191200
pub fn repr(&self, jen: &PyObject, id: usize, vm: &VirtualMachine) -> String {
192201
format!(
193202
"<{} object {} at {:#x}>",

crates/vm/src/protocol/object.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
use crate::{
55
AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
66
builtins::{
7-
PyAsyncGen, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple,
8-
PyTupleRef, PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr,
7+
PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, PyTupleRef,
8+
PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr,
99
},
1010
common::{hash::PyHash, str::to_ascii},
1111
convert::{ToPyObject, ToPyResult},
@@ -87,11 +87,37 @@ impl PyObject {
8787

8888
// PyObject *PyObject_GetAIter(PyObject *o)
8989
pub fn get_aiter(&self, vm: &VirtualMachine) -> PyResult {
90-
if self.downcastable::<PyAsyncGen>() {
91-
vm.call_special_method(self, identifier!(vm, __aiter__), ())
92-
} else {
93-
Err(vm.new_type_error("wrong argument type"))
90+
use crate::builtins::PyCoroutine;
91+
92+
// Check if object has __aiter__ method
93+
let aiter_method = self.class().get_attr(identifier!(vm, __aiter__));
94+
let Some(_aiter_method) = aiter_method else {
95+
return Err(vm.new_type_error(format!(
96+
"'{}' object is not an async iterable",
97+
self.class().name()
98+
)));
99+
};
100+
101+
// Call __aiter__
102+
let iterator = vm.call_special_method(self, identifier!(vm, __aiter__), ())?;
103+
104+
// Check that __aiter__ did not return a coroutine
105+
if iterator.downcast_ref::<PyCoroutine>().is_some() {
106+
return Err(vm.new_type_error(
107+
"'async_iterator' object cannot be interpreted as an async iterable; \
108+
perhaps you forgot to call aiter()?",
109+
));
110+
}
111+
112+
// Check that the result is an async iterator (has __anext__)
113+
if !iterator.class().has_attr(identifier!(vm, __anext__)) {
114+
return Err(vm.new_type_error(format!(
115+
"'{}' object is not an async iterator",
116+
iterator.class().name()
117+
)));
94118
}
119+
120+
Ok(iterator)
95121
}
96122

97123
pub fn has_attr<'a>(&self, attr_name: impl AsPyStr<'a>, vm: &VirtualMachine) -> PyResult<bool> {

0 commit comments

Comments
 (0)