Skip to content

Commit 92aa9db

Browse files
committed
Align GetAwaitable to Python 3.14.2
1 parent 4ca2da4 commit 92aa9db

File tree

7 files changed

+121
-80
lines changed

7 files changed

+121
-80
lines changed

Lib/test/test_asyncio/test_locks.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,32 @@
1414
r'(, value:\d)?'
1515
r'(, waiters:\d+)?'
1616
r'(, waiters:\d+\/\d+)?' # barrier
17-
r')\]>\Z'
17+
r')\]>\z'
1818
)
1919
RGX_REPR = re.compile(STR_RGX_REPR)
2020

2121

2222
def tearDownModule():
23-
asyncio.set_event_loop_policy(None)
23+
asyncio.events._set_event_loop_policy(None)
2424

2525

2626
class LockTests(unittest.IsolatedAsyncioTestCase):
2727

2828
async def test_repr(self):
2929
lock = asyncio.Lock()
30-
self.assertTrue(repr(lock).endswith('[unlocked]>'))
30+
self.assertEndsWith(repr(lock), '[unlocked]>')
3131
self.assertTrue(RGX_REPR.match(repr(lock)))
3232

3333
await lock.acquire()
34-
self.assertTrue(repr(lock).endswith('[locked]>'))
34+
self.assertEndsWith(repr(lock), '[locked]>')
3535
self.assertTrue(RGX_REPR.match(repr(lock)))
3636

3737
async def test_lock(self):
3838
lock = asyncio.Lock()
3939

4040
with self.assertRaisesRegex(
4141
TypeError,
42-
"object Lock can't be used in 'await' expression"
42+
"'Lock' object can't be awaited"
4343
):
4444
await lock
4545

@@ -77,7 +77,7 @@ async def test_lock_by_with_statement(self):
7777
self.assertFalse(lock.locked())
7878
with self.assertRaisesRegex(
7979
TypeError,
80-
r"object \w+ can't be used in 'await' expression"
80+
r"'\w+' object can't be awaited"
8181
):
8282
with await lock:
8383
pass
@@ -286,12 +286,12 @@ class EventTests(unittest.IsolatedAsyncioTestCase):
286286

287287
def test_repr(self):
288288
ev = asyncio.Event()
289-
self.assertTrue(repr(ev).endswith('[unset]>'))
289+
self.assertEndsWith(repr(ev), '[unset]>')
290290
match = RGX_REPR.match(repr(ev))
291291
self.assertEqual(match.group('extras'), 'unset')
292292

293293
ev.set()
294-
self.assertTrue(repr(ev).endswith('[set]>'))
294+
self.assertEndsWith(repr(ev), '[set]>')
295295
self.assertTrue(RGX_REPR.match(repr(ev)))
296296

297297
ev._waiters.append(mock.Mock())
@@ -916,11 +916,11 @@ def test_initial_value_zero(self):
916916

917917
async def test_repr(self):
918918
sem = asyncio.Semaphore()
919-
self.assertTrue(repr(sem).endswith('[unlocked, value:1]>'))
919+
self.assertEndsWith(repr(sem), '[unlocked, value:1]>')
920920
self.assertTrue(RGX_REPR.match(repr(sem)))
921921

922922
await sem.acquire()
923-
self.assertTrue(repr(sem).endswith('[locked]>'))
923+
self.assertEndsWith(repr(sem), '[locked]>')
924924
self.assertTrue('waiters' not in repr(sem))
925925
self.assertTrue(RGX_REPR.match(repr(sem)))
926926

@@ -941,7 +941,7 @@ async def test_semaphore(self):
941941

942942
with self.assertRaisesRegex(
943943
TypeError,
944-
"object Semaphore can't be used in 'await' expression",
944+
"'Semaphore' object can't be awaited",
945945
):
946946
await sem
947947

@@ -1194,14 +1194,14 @@ async def c3(result):
11941194
self.assertEqual([2, 3], result)
11951195

11961196
async def test_acquire_fifo_order_4(self):
1197-
# Test that a successfule `acquire()` will wake up multiple Tasks
1197+
# Test that a successful `acquire()` will wake up multiple Tasks
11981198
# that were waiting in the Semaphore queue due to FIFO rules.
11991199
sem = asyncio.Semaphore(0)
12001200
result = []
12011201
count = 0
12021202

12031203
async def c1(result):
1204-
# First task immediatlly waits for semaphore. It will be awoken by c2.
1204+
# First task immediately waits for semaphore. It will be awoken by c2.
12051205
self.assertEqual(sem._value, 0)
12061206
await sem.acquire()
12071207
# We should have woken up all waiting tasks now.
@@ -1270,7 +1270,7 @@ async def test_barrier(self):
12701270
self.assertIn("filling", repr(barrier))
12711271
with self.assertRaisesRegex(
12721272
TypeError,
1273-
"object Barrier can't be used in 'await' expression",
1273+
"'Barrier' object can't be awaited",
12741274
):
12751275
await barrier
12761276

@@ -1475,13 +1475,13 @@ async def coro():
14751475
# first time waiting
14761476
await barrier.wait()
14771477

1478-
# after wainting once for all tasks
1478+
# after waiting once for all tasks
14791479
if rewait_n > 0:
14801480
rewait_n -= 1
14811481
# wait again only for rewait tasks
14821482
await barrier.wait()
14831483
else:
1484-
# wait for end of draining state`
1484+
# wait for end of draining state
14851485
await barrier_nowaiting.wait()
14861486
# wait for other waiting tasks
14871487
await barrier.wait()
@@ -1780,7 +1780,7 @@ async def coro():
17801780
self.assertEqual(barrier.n_waiting, 0)
17811781

17821782
async def test_abort_barrier_when_exception_then_resetting(self):
1783-
# test from threading.Barrier: see `lock_tests.test_abort_and_reset``
1783+
# test from threading.Barrier: see `lock_tests.test_abort_and_reset`
17841784
barrier1 = asyncio.Barrier(self.N)
17851785
barrier2 = asyncio.Barrier(self.N)
17861786
results1 = []

Lib/test/test_asyncio/test_pep492.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212

1313
def tearDownModule():
14-
asyncio.set_event_loop_policy(None)
14+
asyncio.events._set_event_loop_policy(None)
1515

1616

1717
# Test that asyncio.iscoroutine() uses collections.abc.Coroutine
@@ -77,7 +77,7 @@ async def test(lock):
7777
self.assertFalse(lock.locked())
7878
with self.assertRaisesRegex(
7979
TypeError,
80-
"can't be used in 'await' expression"
80+
"can't be awaited"
8181
):
8282
with await lock:
8383
pass
@@ -124,9 +124,10 @@ def foo(): yield
124124

125125
self.assertFalse(asyncio.iscoroutine(foo()))
126126

127-
128127
def test_iscoroutinefunction(self):
129128
async def foo(): pass
129+
# TODO: RUSTPYTHON; no DeprecationWarning for iscoroutinefunction
130+
# with self.assertWarns(DeprecationWarning):
130131
self.assertTrue(asyncio.iscoroutinefunction(foo))
131132

132133
def test_async_def_coroutines(self):

crates/codegen/src/compile.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,7 +1465,7 @@ impl Compiler {
14651465

14661466
// For async with, await the result
14671467
if matches!(info.fb_type, FBlockType::AsyncWith) {
1468-
emit!(self, Instruction::GetAwaitable);
1468+
emit!(self, Instruction::GetAwaitable { arg: 2 });
14691469
self.emit_load_const(ConstantData::None);
14701470
self.compile_yield_from_sequence(true)?;
14711471
}
@@ -4773,7 +4773,7 @@ impl Compiler {
47734773
// bound_aenter is already bound, call with NULL self_or_null
47744774
emit!(self, Instruction::PushNull); // [bound_aexit, bound_aenter, NULL]
47754775
emit!(self, Instruction::Call { nargs: 0 }); // [bound_aexit, awaitable]
4776-
emit!(self, Instruction::GetAwaitable);
4776+
emit!(self, Instruction::GetAwaitable { arg: 1 });
47774777
self.emit_load_const(ConstantData::None);
47784778
self.compile_yield_from_sequence(true)?;
47794779
} else {
@@ -4854,7 +4854,7 @@ impl Compiler {
48544854
self.emit_load_const(ConstantData::None);
48554855
emit!(self, Instruction::Call { nargs: 3 });
48564856
if is_async {
4857-
emit!(self, Instruction::GetAwaitable);
4857+
emit!(self, Instruction::GetAwaitable { arg: 2 });
48584858
self.emit_load_const(ConstantData::None);
48594859
self.compile_yield_from_sequence(true)?;
48604860
}
@@ -4899,7 +4899,7 @@ impl Compiler {
48994899
emit!(self, Instruction::WithExceptStart);
49004900

49014901
if is_async {
4902-
emit!(self, Instruction::GetAwaitable);
4902+
emit!(self, Instruction::GetAwaitable { arg: 2 });
49034903
self.emit_load_const(ConstantData::None);
49044904
self.compile_yield_from_sequence(true)?;
49054905
}
@@ -6741,7 +6741,7 @@ impl Compiler {
67416741
return Err(self.error(CodegenErrorType::InvalidAwait));
67426742
}
67436743
self.compile_expression(value)?;
6744-
emit!(self, Instruction::GetAwaitable);
6744+
emit!(self, Instruction::GetAwaitable { arg: 0 });
67456745
self.emit_load_const(ConstantData::None);
67466746
self.compile_yield_from_sequence(true)?;
67476747
}
@@ -7516,7 +7516,7 @@ impl Compiler {
75167516
// Call just created <listcomp> function:
75177517
emit!(self, Instruction::Call { nargs: 1 });
75187518
if is_async_list_set_dict_comprehension {
7519-
emit!(self, Instruction::GetAwaitable);
7519+
emit!(self, Instruction::GetAwaitable { arg: 0 });
75207520
self.emit_load_const(ConstantData::None);
75217521
self.compile_yield_from_sequence(true)?;
75227522
}
@@ -7991,7 +7991,7 @@ impl Compiler {
79917991
emit!(self, Instruction::Call { nargs: 3 });
79927992

79937993
if is_async {
7994-
emit!(self, Instruction::GetAwaitable);
7994+
emit!(self, Instruction::GetAwaitable { arg: 2 });
79957995
self.emit_load_const(ConstantData::None);
79967996
self.compile_yield_from_sequence(true)?;
79977997
}

crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/compiler-core/src/bytecode/instruction.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@ pub enum Instruction {
142142
ForIter {
143143
target: Arg<Label>,
144144
} = 70,
145-
GetAwaitable = 71, // TODO: Make this instruction to hold an oparg
145+
GetAwaitable {
146+
arg: Arg<u32>,
147+
} = 71,
146148
ImportFrom {
147149
idx: Arg<NameIdx>,
148150
} = 72,
@@ -582,7 +584,7 @@ impl InstructionMetadata for Instruction {
582584
}
583585
Self::PopExcept => -1,
584586
Self::PopIter => -1,
585-
Self::GetAwaitable => 0,
587+
Self::GetAwaitable { .. } => 0,
586588
Self::GetAIter => 0,
587589
Self::GetANext => 1,
588590
Self::EndAsyncFor => -2, // pops (awaitable, exc) from stack
@@ -828,7 +830,7 @@ impl InstructionMetadata for Instruction {
828830
Self::FormatWithSpec => w!(FORMAT_WITH_SPEC),
829831
Self::GetAIter => w!(GET_AITER),
830832
Self::GetANext => w!(GET_ANEXT),
831-
Self::GetAwaitable => w!(GET_AWAITABLE),
833+
Self::GetAwaitable { arg } => w!(GET_AWAITABLE, arg),
832834
Self::Reserved => w!(RESERVED),
833835
Self::GetIter => w!(GET_ITER),
834836
Self::GetLen => w!(GET_LEN),

crates/vm/src/coroutine.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,52 @@ pub fn is_gen_exit(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> bool {
246246
exc.fast_isinstance(vm.ctx.exceptions.generator_exit)
247247
}
248248

249+
/// Get an awaitable iterator from an object.
250+
///
251+
/// Returns the object itself if it's a coroutine or iterable coroutine (generator with
252+
/// CO_ITERABLE_COROUTINE flag). Otherwise calls `__await__()` and validates the result.
253+
pub fn get_awaitable_iter(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
254+
use crate::builtins::{PyCoroutine, PyGenerator};
255+
use crate::protocol::PyIter;
256+
257+
if obj.downcastable::<PyCoroutine>()
258+
|| obj.downcast_ref::<PyGenerator>().is_some_and(|g| {
259+
g.as_coro()
260+
.frame()
261+
.code
262+
.flags
263+
.contains(crate::bytecode::CodeFlags::ITERABLE_COROUTINE)
264+
})
265+
{
266+
return Ok(obj);
267+
}
268+
269+
if let Some(await_method) = vm.get_method(obj.clone(), identifier!(vm, __await__)) {
270+
let result = await_method?.call((), vm)?;
271+
// __await__() must NOT return a coroutine (PEP 492)
272+
if result.downcastable::<PyCoroutine>()
273+
|| result.downcast_ref::<PyGenerator>().is_some_and(|g| {
274+
g.as_coro()
275+
.frame()
276+
.code
277+
.flags
278+
.contains(crate::bytecode::CodeFlags::ITERABLE_COROUTINE)
279+
})
280+
{
281+
return Err(vm.new_type_error("__await__() returned a coroutine".to_owned()));
282+
}
283+
if !PyIter::check(&result) {
284+
return Err(vm.new_type_error(format!(
285+
"__await__() returned non-iterator of type '{}'",
286+
result.class().name()
287+
)));
288+
}
289+
return Ok(result);
290+
}
291+
292+
Err(vm.new_type_error(format!("'{}' object can't be awaited", obj.class().name())))
293+
}
294+
249295
/// Emit DeprecationWarning for the deprecated 3-argument throw() signature.
250296
pub fn warn_deprecated_throw_signature(
251297
exc_val: &OptionalArg,

0 commit comments

Comments
 (0)