From 02f4fbbb627ca6bcddede7b8099fac235febbaf0 Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:59:10 +0300 Subject: [PATCH] Use ordinary fileno lookup in select (#9266) * Use ordinary fileno lookup in select Assisted-by: Codex:GPT-6 * Share select descriptor conversion Assisted-by: Codex:GPT-6 --- crates/stdlib/src/select.rs | 25 +++++++++++++-------- extra_tests/snippets/stdlib_select.py | 32 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/crates/stdlib/src/select.rs b/crates/stdlib/src/select.rs index 06cabef30..a908114f8 100644 --- a/crates/stdlib/src/select.rs +++ b/crates/stdlib/src/select.rs @@ -3,7 +3,8 @@ pub(crate) use decl::module_def; use crate::vm::{ - PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyListRef, + PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, + builtins::{PyInt, PyListRef}, }; use rustpython_host_env::select::{self as host_select, FdSet, RawFd, platform::FD_SETSIZE}; use std::io; @@ -17,14 +18,20 @@ struct Selectable { impl TryFromObject for Selectable { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let fno = obj.try_to_value(vm).or_else(|_| { - let meth = vm.get_method_or_type_error( - obj.clone(), - vm.ctx.interned_str("fileno").unwrap(), - || "select arg must be an int or object with a fileno() method".to_owned(), - )?; - meth.call((), vm)?.try_into_value(vm) - })?; + let fileno; + let fd = if let Some(fd) = obj.downcast_ref::() { + fd + } else { + let meth = vm.get_attribute_opt(&obj, "fileno")?.ok_or_else(|| { + vm.new_type_error("select arg must be an int or object with a fileno() method") + })?; + fileno = meth + .call((), vm)? + .downcast::() + .map_err(|_| vm.new_type_error("fileno() returned a non-integer"))?; + &fileno + }; + let fno = fd.try_to_primitive(vm)?; Ok(Self { obj, fno }) } } diff --git a/extra_tests/snippets/stdlib_select.py b/extra_tests/snippets/stdlib_select.py index 733e8421f..c2fbc1a28 100644 --- a/extra_tests/snippets/stdlib_select.py +++ b/extra_tests/snippets/stdlib_select.py @@ -167,3 +167,35 @@ if hasattr(select, "poll") and hasattr(signal, "setitimer"): assert handled == [signal.SIGALRM], handled idle.close() idle_peer.close() + + +# fileno is an ordinary attribute, not a special method. +class InstanceFileno: + pass + + +class DynamicFileno: + def __getattr__(self, name): + if name == "fileno": + return lookup_reader.fileno + raise AttributeError(name) + + +class IndexOnly: + def __index__(self): + return lookup_reader.fileno() + + +lookup_reader, lookup_writer = socket.socketpair() +try: + lookup_writer.send(b"x") + instance = InstanceFileno() + instance.fileno = lookup_reader.fileno + for obj in (instance, DynamicFileno()): + assert select.select([obj], [], [], 0) == ([obj], [], []) + assert_raises(TypeError, select.select, [IndexOnly()], [], [], 0) + instance.fileno = IndexOnly + assert_raises(TypeError, select.select, [instance], [], [], 0) +finally: + lookup_reader.close() + lookup_writer.close()