Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
34 changes: 17 additions & 17 deletions Lib/test/test_asyncio/test_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,32 @@
r'(, value:\d)?'
r'(, waiters:\d+)?'
r'(, waiters:\d+\/\d+)?' # barrier
r')\]>\Z'
r')\]>\z'
)
RGX_REPR = re.compile(STR_RGX_REPR)


def tearDownModule():
asyncio.set_event_loop_policy(None)
asyncio.events._set_event_loop_policy(None)


class LockTests(unittest.IsolatedAsyncioTestCase):

async def test_repr(self):
lock = asyncio.Lock()
self.assertTrue(repr(lock).endswith('[unlocked]>'))
self.assertEndsWith(repr(lock), '[unlocked]>')
self.assertTrue(RGX_REPR.match(repr(lock)))

await lock.acquire()
self.assertTrue(repr(lock).endswith('[locked]>'))
self.assertEndsWith(repr(lock), '[locked]>')
self.assertTrue(RGX_REPR.match(repr(lock)))

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

with self.assertRaisesRegex(
TypeError,
"object Lock can't be used in 'await' expression"
"'Lock' object can't be awaited"
):
await lock

Expand Down Expand Up @@ -77,7 +77,7 @@ async def test_lock_by_with_statement(self):
self.assertFalse(lock.locked())
with self.assertRaisesRegex(
TypeError,
r"object \w+ can't be used in 'await' expression"
r"'\w+' object can't be awaited"
):
with await lock:
pass
Expand Down Expand Up @@ -286,12 +286,12 @@ class EventTests(unittest.IsolatedAsyncioTestCase):

def test_repr(self):
ev = asyncio.Event()
self.assertTrue(repr(ev).endswith('[unset]>'))
self.assertEndsWith(repr(ev), '[unset]>')
match = RGX_REPR.match(repr(ev))
self.assertEqual(match.group('extras'), 'unset')

ev.set()
self.assertTrue(repr(ev).endswith('[set]>'))
self.assertEndsWith(repr(ev), '[set]>')
self.assertTrue(RGX_REPR.match(repr(ev)))

ev._waiters.append(mock.Mock())
Expand Down Expand Up @@ -916,11 +916,11 @@ def test_initial_value_zero(self):

async def test_repr(self):
sem = asyncio.Semaphore()
self.assertTrue(repr(sem).endswith('[unlocked, value:1]>'))
self.assertEndsWith(repr(sem), '[unlocked, value:1]>')
self.assertTrue(RGX_REPR.match(repr(sem)))

await sem.acquire()
self.assertTrue(repr(sem).endswith('[locked]>'))
self.assertEndsWith(repr(sem), '[locked]>')
self.assertTrue('waiters' not in repr(sem))
self.assertTrue(RGX_REPR.match(repr(sem)))

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

with self.assertRaisesRegex(
TypeError,
"object Semaphore can't be used in 'await' expression",
"'Semaphore' object can't be awaited",
):
await sem

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

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

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

Expand Down Expand Up @@ -1475,13 +1475,13 @@ async def coro():
# first time waiting
await barrier.wait()

# after wainting once for all tasks
# after waiting once for all tasks
if rewait_n > 0:
rewait_n -= 1
# wait again only for rewait tasks
await barrier.wait()
else:
# wait for end of draining state`
# wait for end of draining state
await barrier_nowaiting.wait()
# wait for other waiting tasks
await barrier.wait()
Expand Down Expand Up @@ -1780,7 +1780,7 @@ async def coro():
self.assertEqual(barrier.n_waiting, 0)

async def test_abort_barrier_when_exception_then_resetting(self):
# test from threading.Barrier: see `lock_tests.test_abort_and_reset``
# test from threading.Barrier: see `lock_tests.test_abort_and_reset`
barrier1 = asyncio.Barrier(self.N)
barrier2 = asyncio.Barrier(self.N)
results1 = []
Expand Down
7 changes: 4 additions & 3 deletions Lib/test/test_asyncio/test_pep492.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


def tearDownModule():
asyncio.set_event_loop_policy(None)
asyncio.events._set_event_loop_policy(None)


# Test that asyncio.iscoroutine() uses collections.abc.Coroutine
Expand Down Expand Up @@ -77,7 +77,7 @@ async def test(lock):
self.assertFalse(lock.locked())
with self.assertRaisesRegex(
TypeError,
"can't be used in 'await' expression"
"can't be awaited"
):
with await lock:
pass
Expand Down Expand Up @@ -124,9 +124,10 @@ def foo(): yield

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


def test_iscoroutinefunction(self):
async def foo(): pass
# TODO: RUSTPYTHON; no DeprecationWarning for iscoroutinefunction
# with self.assertWarns(DeprecationWarning):
self.assertTrue(asyncio.iscoroutinefunction(foo))

def test_async_def_coroutines(self):
Expand Down
14 changes: 7 additions & 7 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1465,7 +1465,7 @@ impl Compiler {

// For async with, await the result
if matches!(info.fb_type, FBlockType::AsyncWith) {
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 2 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down Expand Up @@ -4773,7 +4773,7 @@ impl Compiler {
// bound_aenter is already bound, call with NULL self_or_null
emit!(self, Instruction::PushNull); // [bound_aexit, bound_aenter, NULL]
emit!(self, Instruction::Call { nargs: 0 }); // [bound_aexit, awaitable]
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 1 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
} else {
Expand Down Expand Up @@ -4854,7 +4854,7 @@ impl Compiler {
self.emit_load_const(ConstantData::None);
emit!(self, Instruction::Call { nargs: 3 });
if is_async {
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 2 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down Expand Up @@ -4899,7 +4899,7 @@ impl Compiler {
emit!(self, Instruction::WithExceptStart);

if is_async {
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 2 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down Expand Up @@ -6741,7 +6741,7 @@ impl Compiler {
return Err(self.error(CodegenErrorType::InvalidAwait));
}
self.compile_expression(value)?;
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 0 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down Expand Up @@ -7516,7 +7516,7 @@ impl Compiler {
// Call just created <listcomp> function:
emit!(self, Instruction::Call { nargs: 1 });
if is_async_list_set_dict_comprehension {
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 0 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down Expand Up @@ -7991,7 +7991,7 @@ impl Compiler {
emit!(self, Instruction::Call { nargs: 3 });

if is_async {
emit!(self, Instruction::GetAwaitable);
emit!(self, Instruction::GetAwaitable { arg: 2 });
self.emit_load_const(ConstantData::None);
self.compile_yield_from_sequence(true)?;
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions crates/compiler-core/src/bytecode/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ pub enum Instruction {
ForIter {
target: Arg<Label>,
} = 70,
GetAwaitable = 71, // TODO: Make this instruction to hold an oparg
GetAwaitable {
arg: Arg<u32>,
} = 71,
ImportFrom {
idx: Arg<NameIdx>,
} = 72,
Expand Down Expand Up @@ -582,7 +584,7 @@ impl InstructionMetadata for Instruction {
}
Self::PopExcept => -1,
Self::PopIter => -1,
Self::GetAwaitable => 0,
Self::GetAwaitable { .. } => 0,
Self::GetAIter => 0,
Self::GetANext => 1,
Self::EndAsyncFor => -2, // pops (awaitable, exc) from stack
Expand Down Expand Up @@ -828,7 +830,7 @@ impl InstructionMetadata for Instruction {
Self::FormatWithSpec => w!(FORMAT_WITH_SPEC),
Self::GetAIter => w!(GET_AITER),
Self::GetANext => w!(GET_ANEXT),
Self::GetAwaitable => w!(GET_AWAITABLE),
Self::GetAwaitable { arg } => w!(GET_AWAITABLE, arg),
Self::Reserved => w!(RESERVED),
Self::GetIter => w!(GET_ITER),
Self::GetLen => w!(GET_LEN),
Expand Down
46 changes: 46 additions & 0 deletions crates/vm/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,52 @@ pub fn is_gen_exit(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> bool {
exc.fast_isinstance(vm.ctx.exceptions.generator_exit)
}

/// Get an awaitable iterator from an object.
///
/// Returns the object itself if it's a coroutine or iterable coroutine (generator with
/// CO_ITERABLE_COROUTINE flag). Otherwise calls `__await__()` and validates the result.
pub fn get_awaitable_iter(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
use crate::builtins::{PyCoroutine, PyGenerator};
use crate::protocol::PyIter;

if obj.downcastable::<PyCoroutine>()
|| obj.downcast_ref::<PyGenerator>().is_some_and(|g| {
g.as_coro()
.frame()
.code
.flags
.contains(crate::bytecode::CodeFlags::ITERABLE_COROUTINE)
})
{
return Ok(obj);
}

if let Some(await_method) = vm.get_method(obj.clone(), identifier!(vm, __await__)) {
let result = await_method?.call((), vm)?;
// __await__() must NOT return a coroutine (PEP 492)
if result.downcastable::<PyCoroutine>()
|| result.downcast_ref::<PyGenerator>().is_some_and(|g| {
g.as_coro()
.frame()
.code
.flags
.contains(crate::bytecode::CodeFlags::ITERABLE_COROUTINE)
})
{
return Err(vm.new_type_error("__await__() returned a coroutine".to_owned()));
}
if !PyIter::check(&result) {
return Err(vm.new_type_error(format!(
"__await__() returned non-iterator of type '{}'",
result.class().name()
)));
}
return Ok(result);
}

Err(vm.new_type_error(format!("'{}' object can't be awaited", obj.class().name())))
}

/// Emit DeprecationWarning for the deprecated 3-argument throw() signature.
pub fn warn_deprecated_throw_signature(
exc_val: &OptionalArg,
Expand Down
Loading
Loading