--- src/lib.rs.before
+++ src/lib.rs.experiment
@@ -184,8 +184,7 @@
prototype: Py<PyDict>,
asgi: Py<PyDict>,
done: Py<PyAny>,
- task_type: Py<PyAny>,
- task_options: Py<PyDict>,
+ spawn: Py<PyAny>,
connections: RefCell<HashMap<u64, Link>>,
tasks: RefCell<HashMap<u64, Py<PyAny>>>,
next: Cell<u64>,
@@ -606,22 +605,9 @@
let coro = self.app.bind(py).call1((scope, receive, send))?;
// Task's eager mode runs until the first real wait. Preserve embedding
// applications' custom task factories by using their normal loop path.
- let task = if self
- .loop_
- .bind(py)
- .call_method0(pyo3::intern!(py, "get_task_factory"))?
- .is_none()
- && self.eager_left.get() > 0
- {
- self.eager_left.set(self.eager_left.get() - 1);
- self.task_type
- .bind(py)
- .call((coro,), Some(self.task_options.bind(py)))?
- } else {
- self.loop_
- .bind(py)
- .call_method1(pyo3::intern!(py, "create_task"), (coro,))?
- };
+ let eager = self.eager_left.get() > 0;
+ self.eager_left.set(self.eager_left.get().saturating_sub(1));
+ let task = self.spawn.bind(py).call1((coro, eager))?;
let done = Done {
state: Rc::downgrade(self),
request: r.clone(),
@@ -1237,10 +1223,11 @@
let poller = net::Poller::new()?;
poller.watch(listener.as_raw_fd(), 0, libc::EPOLLIN as u32, true)?;
let done = completed::new(py)?;
- let task_type = py.import("asyncio")?.getattr("Task")?.unbind();
- let task_options = PyDict::new(py);
- task_options.set_item("loop", loop_.bind(py))?;
- task_options.set_item("eager_start", true)?;
+ let spawn = py
+ .import("skirr")?
+ .getattr("_task_runner")?
+ .call1((loop_.bind(py),))?
+ .unbind();
Ok(Self {
state: Rc::new(State {
listener,
@@ -1251,8 +1238,7 @@
prototype: prototype.unbind(),
asgi: asgi.unbind(),
done,
- task_type,
- task_options: task_options.unbind(),
+ spawn,
connections: RefCell::new(HashMap::new()),
tasks: RefCell::new(HashMap::new()),
next: Cell::new(0),
--- python/skirr/__init__.py.before
+++ python/skirr/__init__.py.experiment
@@ -14,6 +14,19 @@
for component in name.split('.'):
app = getattr(app, component)
return app
+
+
+def _task_runner(loop):
+ # Python's call specialization avoids rebuilding a kwargs dictionary at
+ # this boundary. The loop's task factory is still checked per request.
+ from asyncio import Task
+
+ def spawn(coro, eager):
+ if eager and loop.get_task_factory() is None:
+ return Task(coro, loop=loop, eager_start=True)
+ return loop.create_task(coro)
+
+ return spawn
class Lifespan: