polars_utils/
python_thread_pool.rs1use pyo3::call::PyCallArgs;
2use pyo3::sync::PyOnceLock;
3use pyo3::types::{PyAnyMethods as _, PyDict};
4use pyo3::{Bound, IntoPyObject, Py, PyAny, PyResult, Python};
5
6#[derive(IntoPyObject)]
7pub struct PyThreadPool(
8 Py<PyAny>,
10);
11
12impl<'py> IntoPyObject<'py> for &'py PyThreadPool {
13 type Output = <&'py Py<PyAny> as IntoPyObject<'py>>::Output;
14 type Target = <&'py Py<PyAny> as IntoPyObject<'py>>::Target;
15 type Error = <&'py Py<PyAny> as IntoPyObject<'py>>::Error;
16
17 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
18 IntoPyObject::into_pyobject(&self.0, py)
19 }
20}
21
22impl PyThreadPool {
23 pub fn new() -> Self {
24 use std::num::NonZeroUsize;
25
26 Python::attach(|py| {
27 let num_threads =
28 std::env::var("POLARS_PYTHON_SCAN_RESOLVE_THREADS").map_or(128, |x| {
29 x.parse::<NonZeroUsize>()
30 .unwrap_or_else(|_| {
31 panic!("invalid value for POLARS_PYTHON_SCAN_RESOLVE_THREADS: {x}")
32 })
33 .get()
34 });
35
36 if polars_config::config().verbose() {
37 eprintln!("python scan_resolve_threadpool threads: {num_threads}")
38 }
39
40 return Self(
41 py_scan_resolve_threadpool_cls(py)
42 .bind(py)
43 .call1((num_threads,))
44 .map(|x| x.unbind())
45 .unwrap(),
46 );
47
48 fn py_scan_resolve_threadpool_cls(py: Python<'_>) -> &'static Py<PyAny> {
49 static CLS: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
50
51 CLS.get_or_init(py, || {
52 py.import("polars._utils.threading")
53 .unwrap()
54 .getattr("PyThreadPool")
55 .unwrap()
56 .unbind()
57 })
58 }
59 })
60 }
61
62 pub fn spawn_call<'a>(
63 &self,
64 py: Python<'a>,
65 function: &Py<PyAny>,
66 args: impl PyCallArgs<'a>,
67 kwargs: Option<&Bound<'a, PyDict>>,
68 ) -> PyResult<Py<PyAny>> {
69 static CLS: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
70
71 CLS.get_or_init(py, || {
72 py.import("polars._utils.threading")
73 .unwrap()
74 .getattr("FnPoolWrap")
75 .unwrap()
76 .unbind()
77 })
78 .call1(py, (function, self))?
79 .call(py, args, kwargs)
80 }
81}
82
83impl Default for PyThreadPool {
84 fn default() -> Self {
85 Self::new()
86 }
87}