diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c9b0d8e7..33f60cb4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,14 +18,14 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: '3.15' - name: Setup SSL libs run: sudo apt-get update -y && sudo apt-get install -y libipc-run-perl && sudo apt-get install pkg-config libssl-dev - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 pypy3.10 pypy3.11 + args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 3.15 pypy3.10 pypy3.11 sccache: 'true' manylinux: auto before-script-linux: | @@ -65,13 +65,13 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: '3.15' architecture: ${{ matrix.target }} - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 + args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 3.15 sccache: 'true' - name: Upload wheels uses: actions/upload-artifact@v4 @@ -96,7 +96,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: '3.15' # The macOS runner images no longer ship a Rust toolchain on PATH, # so install it explicitly (including the cross-compilation target). - name: Setup Rust @@ -108,7 +108,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 pypy3.10 pypy3.11 + args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 3.15 pypy3.10 pypy3.11 sccache: 'true' - name: Upload wheels uses: actions/upload-artifact@v4 @@ -158,13 +158,13 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: '3.15' architecture: x64 - name: Build wheels uses: messense/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 pypy3.10 pypy3.11 + args: --release --out dist -i 3.10 3.11 3.12 3.13 3.14 3.15 pypy3.10 pypy3.11 manylinux: musllinux_1_2 - name: Upload wheels uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 193bc4d1..91ff10b2 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,7 +35,7 @@ jobs: name: ${{matrix.job.os}}-${{matrix.py_version}}-${{ matrix.postgres_version }} strategy: matrix: - py_version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + py_version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] postgres_version: ["14", "15", "16", "17"] job: - os: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index fbbc345a..3624d208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,12 +601,6 @@ dependencies = [ "hashbrown 0.15.4", ] -[[package]] -name = "indoc" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" - [[package]] name = "inventory" version = "0.3.20" @@ -691,15 +685,6 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1076,30 +1061,29 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.25.1" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" +checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b" dependencies = [ "chrono", - "indoc", "inventory", "libc", - "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "rust_decimal 1.37.2", - "unindent", ] [[package]] name = "pyo3-async-runtimes" -version = "0.25.0" -source = "git+https://github.com/psqlpy-python/pyo3-async-runtimes.git?branch=psqlpy#74cd232b0606cd9e0dcab291bd10a8cadf69a1ed" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ef68daa7316a3fac65e5e18b2203f010346de1c1c53456811a2624673ab046" dependencies = [ - "futures", + "futures-channel", + "futures-util", "once_cell", "pin-project-lite", "pyo3", @@ -1108,20 +1092,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.25.1" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" +checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9" dependencies = [ - "once_cell", - "python3-dll-a", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.25.1" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" +checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327" dependencies = [ "libc", "pyo3-build-config", @@ -1129,9 +1111,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.25.1" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50" +checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -1141,26 +1123,16 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.25.1" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc" +checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952" dependencies = [ "heck", "proc-macro2", - "pyo3-build-config", "quote", "syn 2.0.104", ] -[[package]] -name = "python3-dll-a" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d381ef313ae70b4da5f95f8a4de773c6aa5cd28f73adec4b4a31df70b66780d8" -dependencies = [ - "cc", -] - [[package]] name = "quote" version = "1.0.40" @@ -1483,9 +1455,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "thiserror" @@ -1681,12 +1653,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" - [[package]] name = "uuid" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 616bdc5b..ab21b5ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] deadpool-postgres = { git = "https://github.com/psqlpy-python/deadpool.git", branch = "psqlpy" } -pyo3 = { version = "0.25.1", features = [ +pyo3 = { version = "0.29", features = [ "chrono", "experimental-async", "rust_decimal", @@ -19,7 +19,7 @@ pyo3 = { version = "0.25.1", features = [ "multiple-pymethods", "generate-import-lib", ] } -pyo3-async-runtimes = { git = "https://github.com/psqlpy-python/pyo3-async-runtimes.git", branch = "psqlpy", features = [ +pyo3-async-runtimes = { version = "0.29", features = [ "tokio-runtime", ] } diff --git a/README.md b/README.md index b44e7f53..9821be8c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![PyPI - Python Version](https://img.shields.io/badge/PYTHON-3.10_%7C_3.11_%7C_3.12_%7C_3.13_%7C_3.14-blue?style=for-the-badge +[![PyPI - Python Version](https://img.shields.io/badge/PYTHON-3.10_%7C_3.11_%7C_3.12_%7C_3.13_%7C_3.14_%7C_3.15-blue?style=for-the-badge )](https://pypi.org/project/psqlpy/) [![PyPI - Python Version](https://img.shields.io/badge/Tested%20On%20PostgreSQL-14%20%7C%2015%20%7C%2016%20%7C17-2be28a?style=for-the-badge )](https://pypi.org/project/psqlpy/) diff --git a/pyproject.toml b/pyproject.toml index e1e0bef7..2ad8beac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", ] authors = [{ email = "askiselev00@gmail.com" }, { name = "Kiselev Aleksandr" }] maintainers = [{ name = "Kiselev Aleksandr", email = "askiselev00@gmail.com" }] diff --git a/src/connection/impls.rs b/src/connection/impls.rs index 49227350..e8034fd4 100644 --- a/src/connection/impls.rs +++ b/src/connection/impls.rs @@ -578,7 +578,7 @@ impl PSQLPyConnection { // one here for all remaining rows — independent of batch size, not per-row. let first_pp = template.into_prepared_parameters(); let remaining_pp: PSQLPyResult> = if parameters.len() > 1 { - Python::with_gil(|gil| { + Python::attach(|gil| { parameters[1..] .iter() .map(|param_set| { @@ -757,7 +757,7 @@ impl PSQLPyConnection { .fetch_row_raw(querystring, parameters, prepared) .await?; - Python::with_gil(|gil| match result.columns().first() { + Python::attach(|gil| match result.columns().first() { Some(first_column) => postgres_to_py(gil, &result, first_column, 0, &None), None => Ok(gil.None()), }) diff --git a/src/driver/common.rs b/src/driver/common.rs index b6d26946..c897e307 100644 --- a/src/driver/common.rs +++ b/src/driver/common.rs @@ -255,7 +255,7 @@ macro_rules! impl_binary_copy_method { schema_name: Option, ) -> PSQLPyResult { let (db_client, mut bytes_mut) = - Python::with_gil(|gil| -> PSQLPyResult<(Option<_>, BytesMut)> { + Python::attach(|gil| -> PSQLPyResult<(Option<_>, BytesMut)> { let db_client = self_.borrow(gil).conn.clone(); let Some(db_client) = db_client else { @@ -388,7 +388,7 @@ macro_rules! impl_copy_records_method { columns: Option>, schema_name: Option, ) -> PSQLPyResult { - let db_client = Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = Python::attach(|gil| self_.borrow(gil).conn.clone()); let Some(db_client) = db_client else { return Ok(0); @@ -461,7 +461,7 @@ macro_rules! impl_copy_records_method { // Vec>> then re-visit for DTO conversion). let mut chunks: Vec = Vec::new(); - let gil_result: PSQLPyResult<()> = Python::with_gil(|gil| { + let gil_result: PSQLPyResult<()> = Python::attach(|gil| { let n_cols = column_types.len(); let mut buf = BytesMut::with_capacity(COPY_BUFFER_SIZE); // Scratch vec allocated once and cleared between rows (T3#10). diff --git a/src/driver/connection.rs b/src/driver/connection.rs index b3538be2..7cdc503f 100644 --- a/src/driver/connection.rs +++ b/src/driver/connection.rs @@ -111,7 +111,7 @@ pub async fn connect( Ok(db_connection) } -#[pyclass(subclass)] +#[pyclass(subclass, from_py_object)] #[derive(Clone, Debug)] pub struct Connection { pub conn: Option>>, @@ -159,7 +159,7 @@ impl Connection { } async fn __aenter__(self_: Py) -> PSQLPyResult> { - let (db_client, db_pool, pg_config) = pyo3::Python::with_gil(|gil| { + let (db_client, db_pool, pg_config) = pyo3::Python::attach(|gil| { let self_ = self_.borrow(gil); ( self_.conn.clone(), @@ -178,7 +178,7 @@ impl Connection { Ok::(db_pool.get().await?) }) .await??; - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.conn = Some(Arc::new(RwLock::new(PSQLPyConnection::PoolConn( PoolConnection::new(connection, pg_config), @@ -190,21 +190,21 @@ impl Connection { Err(RustPSQLDriverError::ConnectionClosedError) } - #[allow(clippy::unused_async)] + #[allow(clippy::unused_async, clippy::unused_async_trait_impl)] async fn __aexit__( self_: Py, _exception_type: Py, exception: Py, _traceback: Py, ) -> PSQLPyResult<()> { - let (is_exception_none, py_err) = pyo3::Python::with_gil(|gil| { + let (is_exception_none, py_err) = pyo3::Python::attach(|gil| { ( exception.is_none(gil), PyErr::from_value(exception.into_bound(gil)), ) }); - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); std::mem::take(&mut self_.conn); @@ -233,7 +233,7 @@ impl Connection { parameters: Option>, prepared: Option, ) -> PSQLPyResult { - let db_client = pyo3::Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = pyo3::Python::attach(|gil| self_.borrow(gil).conn.clone()); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -262,7 +262,7 @@ impl Connection { /// 1) Connection is closed. /// 2) Cannot execute querystring. pub async fn execute_batch(self_: pyo3::Py, querystring: String) -> PSQLPyResult<()> { - let db_client = pyo3::Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = pyo3::Python::attach(|gil| self_.borrow(gil).conn.clone()); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -290,7 +290,7 @@ impl Connection { prepared: Option, ) -> PSQLPyResult> { let (db_client, py_none) = - pyo3::Python::with_gil(|gil| (self_.borrow(gil).conn.clone(), gil.None().into_any())); + pyo3::Python::attach(|gil| (self_.borrow(gil).conn.clone(), gil.None().into_any())); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -319,7 +319,7 @@ impl Connection { parameters: Option>, prepared: Option, ) -> PSQLPyResult { - let db_client = pyo3::Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = pyo3::Python::attach(|gil| self_.borrow(gil).conn.clone()); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -356,7 +356,7 @@ impl Connection { parameters: Option>, prepared: Option, ) -> PSQLPyResult { - let db_client = pyo3::Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = pyo3::Python::attach(|gil| self_.borrow(gil).conn.clone()); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -386,7 +386,7 @@ impl Connection { parameters: Option>, prepared: Option, ) -> PSQLPyResult> { - let db_client = pyo3::Python::with_gil(|gil| self_.borrow(gil).conn.clone()); + let db_client = pyo3::Python::attach(|gil| self_.borrow(gil).conn.clone()); if let Some(db_client) = db_client { let read_conn_g = db_client.read().await; @@ -427,7 +427,7 @@ impl Connection { #[allow(clippy::needless_pass_by_value)] pub fn close(self_: pyo3::Py) { - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut connection = self_.borrow_mut(gil); if connection.conn.is_some() { std::mem::take(&mut connection.conn); diff --git a/src/driver/connection_pool.rs b/src/driver/connection_pool.rs index d75094ab..1f725e61 100644 --- a/src/driver/connection_pool.rs +++ b/src/driver/connection_pool.rs @@ -376,7 +376,7 @@ impl ConnectionPool { _exception: Py, _traceback: Py, ) { - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { self_.borrow(gil).close(); }); } @@ -405,7 +405,7 @@ impl ConnectionPool { #[must_use] #[allow(clippy::needless_pass_by_value)] pub fn listener(self_: pyo3::Py) -> Listener { - let (pg_config, pool_conf) = pyo3::Python::with_gil(|gil| { + let (pg_config, pool_conf) = pyo3::Python::attach(|gil| { let b_gil = self_.borrow(gil); (b_gil.pg_config.clone(), b_gil.pool_conf.clone()) }); @@ -418,7 +418,7 @@ impl ConnectionPool { /// # Errors /// May return Err Result if cannot get new connection from the pool. pub async fn connection(self_: pyo3::Py) -> PSQLPyResult { - let (db_pool, pg_config) = pyo3::Python::with_gil(|gil| { + let (db_pool, pg_config) = pyo3::Python::attach(|gil| { let slf = self_.borrow(gil); (slf.pool.clone(), slf.pg_config.clone()) }); diff --git a/src/driver/connection_pool_builder.rs b/src/driver/connection_pool_builder.rs index 5915cc5f..78cc97af 100644 --- a/src/driver/connection_pool_builder.rs +++ b/src/driver/connection_pool_builder.rs @@ -78,7 +78,7 @@ impl ConnectionPoolBuilder { /// Set `ca_file` for `ssl_mode` in `PostgreSQL`. fn ca_file(self_: Py, ca_file: String) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.ca_file = Some(ca_file); }); @@ -96,7 +96,7 @@ impl ConnectionPoolBuilder { )); } - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.max_db_pool_size = Some(pool_size); }); @@ -108,7 +108,7 @@ impl ConnectionPoolBuilder { self_: Py, conn_recycling_method: ConnRecyclingMethod, ) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.conn_recycling_method = Some(conn_recycling_method.to_internal()); }); @@ -120,7 +120,7 @@ impl ConnectionPoolBuilder { /// Defaults to the user executing this process. #[must_use] pub fn user(self_: Py, user: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.user(user); }); @@ -130,7 +130,7 @@ impl ConnectionPoolBuilder { /// Sets the password to authenticate with. #[must_use] pub fn password(self_: Py, password: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.password(password); }); @@ -142,7 +142,7 @@ impl ConnectionPoolBuilder { /// Defaults to the user. #[must_use] pub fn dbname(self_: Py, dbname: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.dbname(dbname); }); @@ -152,7 +152,7 @@ impl ConnectionPoolBuilder { /// Sets command line options used to configure the server. #[must_use] pub fn options(self_: Py, options: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.options(options); }); @@ -162,7 +162,7 @@ impl ConnectionPoolBuilder { /// Sets the value of the `application_name` runtime parameter. #[must_use] pub fn application_name(self_: Py, application_name: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.application_name(application_name); }); @@ -174,7 +174,7 @@ impl ConnectionPoolBuilder { /// Defaults to `prefer`. #[must_use] pub fn ssl_mode(self_: Py, ssl_mode: SslMode) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.ssl_mode = Some(ssl_mode); self_.config.ssl_mode(ssl_mode.to_internal()); @@ -189,7 +189,7 @@ impl ConnectionPoolBuilder { /// There must be either no hosts, or the same number of hosts as hostaddrs. #[must_use] pub fn host(self_: Py, host: &str) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.host(host); }); @@ -202,7 +202,7 @@ impl ConnectionPoolBuilder { /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. #[must_use] pub fn hostaddr(self_: Py, hostaddr: IpAddr) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.hostaddr(hostaddr); }); @@ -216,7 +216,7 @@ impl ConnectionPoolBuilder { /// as hosts. #[must_use] pub fn port(self_: Py, port: u16) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.port(port); }); @@ -229,7 +229,7 @@ impl ConnectionPoolBuilder { /// host separately. Defaults to no limit. #[must_use] pub fn connect_timeout(self_: Py, connect_timeout: u64) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -245,7 +245,7 @@ impl ConnectionPoolBuilder { /// on other systems, it has no effect. #[must_use] pub fn tcp_user_timeout(self_: Py, tcp_user_timeout: u64) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -263,7 +263,7 @@ impl ConnectionPoolBuilder { self_: Py, target_session_attrs: TargetSessionAttrs, ) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -277,7 +277,7 @@ impl ConnectionPoolBuilder { /// Defaults to `disable`. #[must_use] pub fn load_balance_hosts(self_: Py, load_balance_hosts: LoadBalanceHosts) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -291,7 +291,7 @@ impl ConnectionPoolBuilder { /// This is ignored for Unix domain socket connections. Defaults to `true`. #[must_use] pub fn keepalives(self_: Py, keepalives: bool) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.keepalives(keepalives); }); @@ -304,7 +304,7 @@ impl ConnectionPoolBuilder { #[must_use] #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_idle(self_: Py, keepalives_idle: u64) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -320,7 +320,7 @@ impl ConnectionPoolBuilder { #[must_use] #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_interval(self_: Py, keepalives_interval: u64) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_ .config @@ -335,7 +335,7 @@ impl ConnectionPoolBuilder { #[must_use] #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_retries(self_: Py, keepalives_retries: u32) -> Py { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.config.keepalives_retries(keepalives_retries); }); diff --git a/src/driver/cursor.rs b/src/driver/cursor.rs index e10598de..3a43b340 100644 --- a/src/driver/cursor.rs +++ b/src/driver/cursor.rs @@ -1,8 +1,6 @@ use std::sync::Arc; -use pyo3::{ - exceptions::PyStopAsyncIteration, pyclass, pymethods, Py, PyAny, PyErr, PyObject, Python, -}; +use pyo3::{exceptions::PyStopAsyncIteration, pyclass, pymethods, Py, PyAny, PyErr, Python}; use tokio::sync::RwLock; use tokio_postgres::{Config, Portal as tp_Portal}; @@ -103,7 +101,7 @@ impl Cursor { #[allow(clippy::single_match_else)] async fn __aenter__(slf: Py) -> PSQLPyResult> { - let (conn, querystring, parameters, statement) = Python::with_gil(|gil| { + let (conn, querystring, parameters, statement) = Python::attach(|gil| { let self_ = slf.borrow(gil); ( self_.conn.clone(), @@ -134,7 +132,7 @@ impl Cursor { } }; - Python::with_gil(|gil| { + Python::attach(|gil| { let mut self_ = slf.borrow_mut(gil); self_.transaction = Some(Arc::new(txid)); @@ -144,7 +142,11 @@ impl Cursor { Ok(slf) } - #[allow(clippy::needless_pass_by_value, clippy::unused_async)] + #[allow( + clippy::needless_pass_by_value, + clippy::unused_async, + clippy::unused_async_trait_impl + )] async fn __aexit__( &mut self, _exception_type: Py, @@ -153,7 +155,7 @@ impl Cursor { ) -> PSQLPyResult<()> { self.close(); - let (is_exc_none, py_err) = pyo3::Python::with_gil(|gil| { + let (is_exc_none, py_err) = pyo3::Python::attach(|gil| { ( exception.is_none(gil), PyErr::from_value(exception.into_bound(gil)), @@ -166,12 +168,12 @@ impl Cursor { Ok(()) } - fn __anext__(&self) -> PSQLPyResult> { + fn __anext__(&self) -> PSQLPyResult>> { let txid = self.transaction.clone(); let portal = self.inner.clone(); let size = self.array_size; - let py_future = Python::with_gil(move |gil| { + let py_future = Python::attach(move |gil| { rustdriver_future(gil, async move { let Some(txid) = &txid else { return Err(RustPSQLDriverError::TransactionClosedError); diff --git a/src/driver/listener/core.rs b/src/driver/listener/core.rs index 28fcbef2..c644ea51 100644 --- a/src/driver/listener/core.rs +++ b/src/driver/listener/core.rs @@ -88,19 +88,19 @@ impl Listener { slf } - #[allow(clippy::unused_async)] + #[allow(clippy::unused_async, clippy::unused_async_trait_impl)] async fn __aenter__(slf: Py) -> PSQLPyResult> { Ok(slf) } - #[allow(clippy::unused_async)] + #[allow(clippy::unused_async, clippy::unused_async_trait_impl)] async fn __aexit__( slf: Py, _exception_type: Py, exception: Py, _traceback: Py, ) -> PSQLPyResult<()> { - let (client, is_exception_none, py_err) = pyo3::Python::with_gil(|gil| { + let (client, is_exception_none, py_err) = pyo3::Python::attach(|gil| { let self_ = slf.borrow(gil); ( self_.connection.db_client(), @@ -110,7 +110,7 @@ impl Listener { }); if client.is_some() { - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut self_ = slf.borrow_mut(gil); std::mem::take(&mut self_.connection); std::mem::take(&mut self_.receiver); @@ -143,7 +143,7 @@ impl Listener { let applied_channels_clone = self.applied_channels.clone(); let connection = self.connection.clone(); - let py_future = Python::with_gil(move |gil| { + let py_future = Python::attach(move |gil| { rustdriver_future(gil, async move { { execute_listen( @@ -249,7 +249,7 @@ impl Listener { } /// TODO: remove clippy ignore after removing async - #[allow(clippy::unused_async)] + #[allow(clippy::unused_async, clippy::unused_async_trait_impl)] async fn shutdown(&mut self) { self.abort_listen(); std::mem::take(&mut self.connection); @@ -264,7 +264,7 @@ impl Listener { return Err(RustPSQLDriverError::ListenerCallbackError); } - let task_locals = Python::with_gil(pyo3_async_runtimes::tokio::get_current_locals)?; + let task_locals = Python::attach(pyo3_async_runtimes::tokio::get_current_locals)?; let listener_callback = ListenerCallback::new(task_locals, callback); diff --git a/src/driver/listener/structs.rs b/src/driver/listener/structs.rs index f557403b..61d6aad9 100644 --- a/src/driver/listener/structs.rs +++ b/src/driver/listener/structs.rs @@ -128,11 +128,11 @@ impl ListenerCallback { connection: Connection, ) -> PSQLPyResult<()> { let (callback, task_locals) = - Python::with_gil(|py| (self.callback.clone(), self.task_locals.clone_ref(py))); + Python::attach(|_py| (self.callback.clone(), self.task_locals.clone())); tokio_runtime() .spawn(pyo3_async_runtimes::tokio::scope(task_locals, async move { - let future = Python::with_gil(|py| { + let future = Python::attach(|py| { let awaitable = callback .call1( py, diff --git a/src/driver/transaction.rs b/src/driver/transaction.rs index 5d9ff9cf..0ff2793d 100644 --- a/src/driver/transaction.rs +++ b/src/driver/transaction.rs @@ -61,7 +61,7 @@ impl Transaction { } async fn __aenter__(self_: Py) -> PSQLPyResult> { - let (isolation_level, read_variant, deferrable, conn) = pyo3::Python::with_gil(|gil| { + let (isolation_level, read_variant, deferrable, conn) = pyo3::Python::attach(|gil| { let self_ = self_.borrow(gil); ( self_.isolation_level, @@ -89,7 +89,7 @@ impl Transaction { exception: Py, _traceback: Py, ) -> PSQLPyResult<()> { - let (conn, is_exception_none, py_err) = pyo3::Python::with_gil(|gil| { + let (conn, is_exception_none, py_err) = pyo3::Python::attach(|gil| { let self_ = self_.borrow(gil); ( self_.conn.clone(), @@ -104,14 +104,14 @@ impl Transaction { let mut write_conn_g = conn.write().await; if is_exception_none { write_conn_g.commit().await?; - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.conn = None; }); Ok(()) } else { write_conn_g.rollback().await?; - pyo3::Python::with_gil(|gil| { + pyo3::Python::attach(|gil| { let mut self_ = self_.borrow_mut(gil); self_.conn = None; }); @@ -320,7 +320,7 @@ impl Transaction { queries: Option>, prepared: Option, ) -> PSQLPyResult> { - let db_client = pyo3::Python::with_gil(|gil| { + let db_client = pyo3::Python::attach(|gil| { let self_ = self_.borrow(gil); self_.conn.clone() @@ -330,9 +330,9 @@ impl Transaction { let conn_read_g = db_client.read().await; let mut futures = vec![]; if let Some(queries) = queries { - let gil_result = pyo3::Python::with_gil(|gil| -> PyResult<()> { + let gil_result = pyo3::Python::attach(|gil| -> PyResult<()> { for single_query in queries.into_bound(gil).iter() { - let query_tuple = single_query.downcast::().map_err(|err| { + let query_tuple = single_query.cast::().map_err(|err| { RustPSQLDriverError::PyToRustValueConversionError(format!( "Cannot cast to tuple: {err}", )) diff --git a/src/driver/utils.rs b/src/driver/utils.rs index e3c0a1f9..91ceb152 100644 --- a/src/driver/utils.rs +++ b/src/driver/utils.rs @@ -226,7 +226,7 @@ pub fn build_manager( /// 1) import inspect /// 2) extract boolean pub fn is_coroutine_function(function: Py) -> PSQLPyResult { - let is_coroutine_function: bool = Python::with_gil(|py| { + let is_coroutine_function: bool = Python::attach(|py| { let inspect = py.import("inspect")?; let is_cor = inspect diff --git a/src/extra_types.rs b/src/extra_types.rs index e82372f7..8493d33b 100644 --- a/src/extra_types.rs +++ b/src/extra_types.rs @@ -25,7 +25,7 @@ pub struct PythonDecimal; pub struct PythonUUID; pub struct PythonEnum; -#[pyclass] +#[pyclass(from_py_object)] #[derive(Clone)] pub struct PgVector(Vec); @@ -46,7 +46,7 @@ impl PgVector { macro_rules! build_python_type { ($st_name:ident, $rust_type:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] pub struct $st_name { inner_value: $rust_type, @@ -82,7 +82,7 @@ build_python_type!(Money, i64); build_python_type!(Float32, f32); build_python_type!(Float64, f64); -#[pyclass] +#[pyclass(from_py_object)] #[derive(Clone)] pub struct Text { inner: String, @@ -106,7 +106,7 @@ impl Text { } } -#[pyclass] +#[pyclass(from_py_object)] #[derive(Clone)] pub struct VarChar { inner: String, @@ -132,7 +132,7 @@ impl VarChar { macro_rules! build_json_py_type { ($st_name:ident, $rust_type:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] pub struct $st_name { inner: $rust_type, @@ -173,7 +173,7 @@ build_json_py_type!(JSON, Value); macro_rules! build_macaddr_type { ($st_name:ident, $rust_type:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] pub struct $st_name { inner: $rust_type, @@ -202,7 +202,7 @@ macro_rules! build_macaddr_type { build_macaddr_type!(MacAddr6, RustMacAddr6); build_macaddr_type!(MacAddr8, RustMacAddr8); -#[pyclass] +#[pyclass(from_py_object)] #[derive(Clone, Debug)] pub struct CustomType { inner: Vec, @@ -225,7 +225,7 @@ impl CustomType { macro_rules! build_geo_type { ($st_name:ident, $rust_type:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] pub struct $st_name { inner: $rust_type, @@ -326,7 +326,7 @@ impl Circle { macro_rules! build_array_type { ($st_name:ident, $kind:path, $elem_kind:path) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] pub struct $st_name { inner: Py, @@ -357,7 +357,7 @@ macro_rules! build_array_type { /// # Errors /// May return Err Result if cannot convert sequence to array. pub fn _convert_to_python_dto(&self, elem_type: &Type) -> PSQLPyResult { - return Python::with_gil(|gil| { + return Python::attach(|gil| { let binding = &self.inner; let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>( binding.bind(gil), diff --git a/src/options.rs b/src/options.rs index 388fdaa7..6afd31b8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -3,7 +3,7 @@ use std::time::Duration; use deadpool_postgres::RecyclingMethod; use pyo3::{pyclass, pymethods}; -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum ConnRecyclingMethod { Fast, @@ -22,7 +22,7 @@ impl ConnRecyclingMethod { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum LoadBalanceHosts { /// Make connection attempts to hosts in the order provided. @@ -41,7 +41,7 @@ impl LoadBalanceHosts { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum TargetSessionAttrs { /// No special properties are required. @@ -63,7 +63,7 @@ impl TargetSessionAttrs { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq, Debug)] pub enum SslMode { /// Do not use TLS. @@ -99,7 +99,7 @@ impl SslMode { } } -#[pyclass] +#[pyclass(from_py_object)] #[derive(Clone, Copy)] pub struct KeepaliveConfig { pub idle: Duration, @@ -121,7 +121,7 @@ impl KeepaliveConfig { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum CopyCommandFormat { TEXT, @@ -140,7 +140,7 @@ impl CopyCommandFormat { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq, Debug)] pub enum IsolationLevel { ReadUncommitted, @@ -162,14 +162,14 @@ impl IsolationLevel { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq, Debug)] pub enum ReadVariant { ReadOnly, ReadWrite, } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum SynchronousCommit { /// As the name indicates, the commit acknowledgment can come before diff --git a/src/query_result.rs b/src/query_result.rs index 451cd0ba..f1b78705 100644 --- a/src/query_result.rs +++ b/src/query_result.rs @@ -342,7 +342,7 @@ impl Record { } // Slice - if let Ok(slice) = key.downcast::() { + if let Ok(slice) = key.cast::() { // Safe: len() <= isize::MAX on any platform we target let indices = slice.indices(self.values.len() as isize)?; let mut result: Vec> = Vec::new(); diff --git a/src/row_factories.rs b/src/row_factories.rs index e68decd5..0939daaf 100644 --- a/src/row_factories.rs +++ b/src/row_factories.rs @@ -9,7 +9,7 @@ use crate::exceptions::rust_errors::{PSQLPyResult, RustPSQLDriverError}; #[pyfunction] #[allow(clippy::needless_pass_by_value)] fn tuple_row(py: Python<'_>, dict_: Py) -> PSQLPyResult> { - let dict_ = dict_.downcast_bound::(py).map_err(|_| { + let dict_ = dict_.cast_bound::(py).map_err(|_| { RustPSQLDriverError::RustToPyValueConversionError( "as_tuple accepts only dict as a parameter".into(), ) @@ -33,7 +33,7 @@ impl class_row { #[allow(clippy::needless_pass_by_value)] fn __call__(&self, py: Python<'_>, dict_: Py) -> PSQLPyResult> { - let dict_ = dict_.downcast_bound::(py).map_err(|_| { + let dict_ = dict_.cast_bound::(py).map_err(|_| { RustPSQLDriverError::RustToPyValueConversionError( "as_tuple accepts only dict as a parameter".into(), ) diff --git a/src/runtime.rs b/src/runtime.rs index ee6281de..ded719fb 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -21,7 +21,7 @@ pub fn tokio_runtime() -> &'static tokio::runtime::Runtime { pub fn rustdriver_future(py: Python<'_>, future: F) -> PSQLPyResult> where F: Future> + Send + 'static, - T: for<'py> IntoPyObject<'py>, + T: for<'py> IntoPyObject<'py> + Send + 'static, { let res = pyo3_async_runtimes::tokio::future_into_py(py, async { future.await.map_err(Into::into) }) diff --git a/src/statement/parameters.rs b/src/statement/parameters.rs index e1af46da..a6258f91 100644 --- a/src/statement/parameters.rs +++ b/src/statement/parameters.rs @@ -2,10 +2,9 @@ use std::iter::zip; use postgres_types::{ToSql, Type}; use pyo3::{ - conversion::FromPyObjectBound, pyclass, pymethods, types::{PyAnyMethods, PyMapping}, - Py, PyObject, PyTypeCheck, Python, + FromPyObject, Py, PyAny, PyTypeCheck, Python, }; use crate::{ @@ -18,7 +17,7 @@ use crate::{ pub type QueryParameter = dyn ToSql + Sync; -#[pyclass] +#[pyclass(from_py_object)] #[derive(Default, Clone, Debug)] pub struct Column { name: String, @@ -46,14 +45,14 @@ impl Column { } pub(crate) struct ParametersBuilder { - parameters: Option, + parameters: Option>, types: Option>, columns: Vec, } impl ParametersBuilder { pub fn new( - parameters: Option<&PyObject>, + parameters: Option<&Py>, types: Option>, columns: Vec, ) -> Self { @@ -73,7 +72,7 @@ impl ParametersBuilder { } let prepared_parameters = - Python::with_gil(|gil| self.prepare_parameters(gil, parameters_names))?; + Python::attach(|gil| self.prepare_parameters(gil, parameters_names))?; Ok(prepared_parameters) } @@ -99,7 +98,7 @@ impl ParametersBuilder { return Ok(PreparedParameters::default()); } - let sequence_typed = self.as_type::>(gil); + let sequence_typed = self.as_type::>>(gil); // Empty sequence: no conversion work to do. if sequence_typed.as_ref().is_some_and(Vec::is_empty) { @@ -136,7 +135,7 @@ impl ParametersBuilder { )) } - fn as_type FromPyObjectBound<'a, 'py>>(&self, gil: Python<'_>) -> Option { + fn as_type FromPyObject<'a, 'py>>(&self, gil: Python<'_>) -> Option { if let Some(parameters) = &self.parameters { let extracted_param = parameters.extract::(gil); @@ -152,7 +151,7 @@ impl ParametersBuilder { fn downcast_as(&self, gil: Python<'_>) -> Option> { if let Some(parameters) = &self.parameters { - let extracted_param = parameters.downcast_bound::(gil); + let extracted_param = parameters.cast_bound::(gil); if let Ok(extracted_param) = extracted_param { return Some(extracted_param.clone().unbind()); @@ -232,8 +231,8 @@ impl MappingParametersBuilder { &self, gil: Python<'_>, parameters_names: Vec, - ) -> PSQLPyResult> { - let mut params_as_pyobject: Vec = vec![]; + ) -> PSQLPyResult>> { + let mut params_as_pyobject: Vec> = vec![]; for param_name in parameters_names { match self.map_parameters.bind(gil).get_item(¶m_name) { @@ -251,13 +250,13 @@ impl MappingParametersBuilder { } pub(crate) struct SequenceParametersBuilder { - seq_parameters: Vec, + seq_parameters: Vec>, types: Option>, columns: Vec, } impl SequenceParametersBuilder { - fn new(seq_parameters: Vec, types: Option>, columns: Vec) -> Self { + fn new(seq_parameters: Vec>, types: Option>, columns: Vec) -> Self { Self { seq_parameters, types, diff --git a/src/statement/query.rs b/src/statement/query.rs index 5db5ad6f..120ce4a8 100644 --- a/src/statement/query.rs +++ b/src/statement/query.rs @@ -50,7 +50,7 @@ impl QueryString { parameters_names.push(parameter_idx.clone()); counter += 1; - format!("${}", &counter) + format!("${counter}") }); self.converted_qs = Some(ConvertedQueryString::new(result.into(), parameters_names)); diff --git a/src/statement/statement_builder.rs b/src/statement/statement_builder.rs index e35bb656..c0dc57b6 100644 --- a/src/statement/statement_builder.rs +++ b/src/statement/statement_builder.rs @@ -1,4 +1,4 @@ -use pyo3::PyObject; +use pyo3::{Py, PyAny}; use tokio_postgres::Statement; use crate::{ @@ -14,7 +14,7 @@ use super::{ pub struct StatementBuilder<'a> { querystring: &'a String, - parameters: &'a Option, + parameters: &'a Option>, inner_conn: &'a PSQLPyConnection, prepared: bool, } @@ -23,7 +23,7 @@ impl<'a> StatementBuilder<'a> { #[must_use] pub fn new( querystring: &'a String, - parameters: &'a Option, + parameters: &'a Option>, inner_conn: &'a PSQLPyConnection, prepared: Option, ) -> Self { diff --git a/src/value_converter/consts.rs b/src/value_converter/consts.rs index fe4b7b34..1b8dd22a 100644 --- a/src/value_converter/consts.rs +++ b/src/value_converter/consts.rs @@ -1,19 +1,19 @@ use pyo3::{ - sync::GILOnceCell, + sync::PyOnceLock, types::{PyAnyMethods, PyType}, Bound, Py, PyResult, Python, }; pub static KWARGS_PARAMS_REGEXP: &str = r"\$\(([^)]+)\)p"; -pub static DECIMAL_CLS: GILOnceCell> = GILOnceCell::new(); -pub static TIMEDELTA_CLS: GILOnceCell> = GILOnceCell::new(); +pub static DECIMAL_CLS: PyOnceLock> = PyOnceLock::new(); +pub static TIMEDELTA_CLS: PyOnceLock> = PyOnceLock::new(); #[allow(clippy::missing_errors_doc)] pub fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { DECIMAL_CLS .get_or_try_init(py, || { - let type_object = py.import("decimal")?.getattr("Decimal")?.downcast_into()?; + let type_object = py.import("decimal")?.getattr("Decimal")?.cast_into()?; Ok(type_object.unbind()) }) .map(|ty| ty.bind(py)) @@ -23,10 +23,7 @@ pub fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { pub fn get_timedelta_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { TIMEDELTA_CLS .get_or_try_init(py, || { - let type_object = py - .import("datetime")? - .getattr("timedelta")? - .downcast_into()?; + let type_object = py.import("datetime")?.getattr("timedelta")?.cast_into()?; Ok(type_object.unbind()) }) .map(|ty| ty.bind(py)) diff --git a/src/value_converter/dto/converter_impls.rs b/src/value_converter/dto/converter_impls.rs index 14e436de..ccf25c14 100644 --- a/src/value_converter/dto/converter_impls.rs +++ b/src/value_converter/dto/converter_impls.rs @@ -33,7 +33,11 @@ macro_rules! construct_simple_type_converter { ($match_type:ty, $kind:path) => { impl ToPythonDTO for $match_type { fn to_python_dto(python_param: &Bound<'_, PyAny>) -> PSQLPyResult { - Ok($kind(python_param.extract::<$match_type>()?)) + Ok($kind( + python_param + .extract::<$match_type>() + .map_err(pyo3::PyErr::from)?, + )) } } }; @@ -97,7 +101,12 @@ macro_rules! construct_extra_type_converter { ($match_type:ty, $kind:path) => { impl ToPythonDTO for $match_type { fn to_python_dto(python_param: &Bound<'_, PyAny>) -> PSQLPyResult { - Ok($kind(python_param.extract::<$match_type>()?.inner())) + Ok($kind( + python_param + .extract::<$match_type>() + .map_err(pyo3::PyErr::from)? + .inner(), + )) } } }; @@ -187,7 +196,8 @@ macro_rules! construct_array_type_converter { impl ToPythonDTO for $match_type { fn to_python_dto(python_param: &Bound<'_, PyAny>) -> PSQLPyResult { python_param - .extract::<$match_type>()? + .extract::<$match_type>() + .map_err(pyo3::PyErr::from)? ._convert_to_python_dto(&Self::element_type()) } } diff --git a/src/value_converter/from_python.rs b/src/value_converter/from_python.rs index 282e0f04..5a6066e3 100644 --- a/src/value_converter/from_python.rs +++ b/src/value_converter/from_python.rs @@ -7,7 +7,7 @@ use postgres_types::Type; use std::net::IpAddr; use pyo3::{ - sync::GILOnceCell, + sync::PyOnceLock, types::{ PyAnyMethods, PyBool, PyBytes, PyDate, PyDateTime, PyDelta, PyDict, PyFloat, PyInt, PyList, PySequence, PySet, PyString, PyTime, PyTuple, PyType, @@ -16,9 +16,9 @@ use pyo3::{ }; /// Cached `uuid.UUID` type object for O(1) pointer-equality type dispatch. -static UUID_TYPE: GILOnceCell> = GILOnceCell::new(); +static UUID_TYPE: PyOnceLock> = PyOnceLock::new(); /// Cached `decimal.Decimal` type object for O(1) pointer-equality type dispatch. -static DECIMAL_TYPE: GILOnceCell> = GILOnceCell::new(); +static DECIMAL_TYPE: PyOnceLock> = PyOnceLock::new(); fn uuid_type(py: Python<'_>) -> PSQLPyResult> { UUID_TYPE @@ -26,7 +26,7 @@ fn uuid_type(py: Python<'_>) -> PSQLPyResult> { pyo3::types::PyModule::import(py, "uuid") .and_then(|m| m.getattr("UUID")) .and_then(|t| { - t.downcast::() + t.cast::() .map(|t| t.clone().unbind()) .map_err(Into::into) }) @@ -45,7 +45,7 @@ fn decimal_type(py: Python<'_>) -> PSQLPyResult> { pyo3::types::PyModule::import(py, "decimal") .and_then(|m| m.getattr("Decimal")) .and_then(|t| { - t.downcast::() + t.cast::() .map(|t| t.clone().unbind()) .map_err(Into::into) }) @@ -557,7 +557,7 @@ pub fn py_sequence_into_postgres_array( type_: &Type, ) -> PSQLPyResult> { let mut py_seq = parameter - .downcast::() + .cast::() .map_err(|_| { RustPSQLDriverError::PyToRustValueConversionError( "PostgreSQL ARRAY type can be made only from python Sequence".into(), @@ -584,7 +584,7 @@ pub fn py_sequence_into_postgres_array( continue_iteration = false; continue; } - let possible_inner_seq = first_seq_elem.downcast::(); + let possible_inner_seq = first_seq_elem.cast::(); match possible_inner_seq { Ok(possible_inner_seq) => { @@ -617,7 +617,7 @@ pub fn py_sequence_into_flat_vec( parameter: &Bound, type_: &Type, ) -> PSQLPyResult> { - let py_seq = parameter.downcast::().map_err(|_| { + let py_seq = parameter.cast::().map_err(|_| { RustPSQLDriverError::PyToRustValueConversionError( "PostgreSQL ARRAY type can be made only from python Sequence".into(), ) @@ -635,7 +635,7 @@ pub fn py_sequence_into_flat_vec( continue; } - let possible_next_seq = ok_seq_elem.downcast::(); + let possible_next_seq = ok_seq_elem.cast::(); if let Ok(next_seq) = possible_next_seq { let mut next_vec = py_sequence_into_flat_vec(next_seq, type_)?; @@ -656,7 +656,7 @@ pub fn py_sequence_into_flat_vec( /// May return error if cannot convert Python type into Rust one. /// May return error if parameters type isn't correct. fn convert_py_to_rust_coord_values(parameters: Vec>) -> PSQLPyResult> { - Python::with_gil(|gil| { + Python::attach(|gil| { let mut coord_values_vec: Vec = vec![]; for one_parameter in parameters { @@ -712,7 +712,7 @@ pub fn build_geo_coords( ) -> PSQLPyResult> { let mut result_vec: Vec = vec![]; - result_vec = Python::with_gil(|gil| { + result_vec = Python::attach(|gil| { let bind_py_parameters = py_parameters.bind(gil); let parameters = py_sequence_to_rust(bind_py_parameters)?; @@ -784,7 +784,7 @@ pub fn build_flat_geo_coords( py_parameters: Py, allowed_length_option: Option, ) -> PSQLPyResult> { - Python::with_gil(|gil| { + Python::attach(|gil| { let allowed_length = allowed_length_option.unwrap_or_default(); let bind_py_parameters = py_parameters.bind(gil); @@ -821,7 +821,7 @@ fn py_sequence_to_rust(bind_parameters: &Bound) -> PSQLPyResult> = vec![]; if bind_parameters.is_instance_of::() { - let bind_pyset_parameters = bind_parameters.downcast::().unwrap(); + let bind_pyset_parameters = bind_parameters.cast::().unwrap(); for one_parameter in bind_pyset_parameters { let extracted_parameter = one_parameter.extract::>().map_err(|_| { diff --git a/src/value_converter/models/serde_value.rs b/src/value_converter/models/serde_value.rs index 682cbcd0..9bb747d5 100644 --- a/src/value_converter/models/serde_value.rs +++ b/src/value_converter/models/serde_value.rs @@ -4,7 +4,7 @@ use serde_json::{Map, Value}; use pyo3::{ types::{PyAnyMethods, PyDict, PyDictMethods, PyList, PyListMethods, PyTuple, PyTupleMethods}, - Bound, FromPyObject, IntoPyObject, PyAny, PyResult, Python, + Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python, }; use tokio_postgres::types::Type; @@ -23,9 +23,11 @@ use crate::{ #[derive(Clone)] pub struct InternalSerdeValue(Value); -impl<'a> FromPyObject<'a> for InternalSerdeValue { - fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult { - let serde_value = build_serde_value(ob)?; +impl<'py> FromPyObject<'_, 'py> for InternalSerdeValue { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { + let serde_value = build_serde_value(&ob)?; Ok(InternalSerdeValue(serde_value)) } @@ -71,12 +73,12 @@ fn serde_value_for_json_child(item: &Bound<'_, PyAny>) -> PSQLPyResult { fn serde_value_from_sequence(bind_value: &Bound<'_, PyAny>) -> PSQLPyResult { let mut result_vec: Vec = Vec::new(); - if let Ok(py_list) = bind_value.downcast::() { + if let Ok(py_list) = bind_value.cast::() { result_vec.reserve(py_list.len()); for item in py_list.iter() { result_vec.push(serde_value_for_json_child(&item)?); } - } else if let Ok(py_tuple) = bind_value.downcast::() { + } else if let Ok(py_tuple) = bind_value.cast::() { result_vec.reserve(py_tuple.len()); for index in 0..py_tuple.len() { let item = py_tuple.get_item(index)?; @@ -92,7 +94,7 @@ fn serde_value_from_sequence(bind_value: &Bound<'_, PyAny>) -> PSQLPyResult) -> PSQLPyResult { - let dict = bind_value.downcast::().map_err(|error| { + let dict = bind_value.cast::().map_err(|error| { RustPSQLDriverError::PyToRustValueConversionError(format!( "Can't cast to inner dict: {error}" )) diff --git a/src/value_converter/models/uuid.rs b/src/value_converter/models/uuid.rs index 1b4a7794..17c4e0fa 100644 --- a/src/value_converter/models/uuid.rs +++ b/src/value_converter/models/uuid.rs @@ -3,7 +3,7 @@ use uuid::Uuid; use pyo3::{ types::{PyAnyMethods, PyString}, - Bound, FromPyObject, IntoPyObject, PyAny, PyResult, Python, + Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python, }; use tokio_postgres::types::Type; @@ -16,8 +16,10 @@ use crate::exceptions::rust_errors::RustPSQLDriverError; #[derive(Clone, Copy)] pub struct InternalUuid(Uuid); -impl<'a> FromPyObject<'a> for InternalUuid { - fn extract_bound(obj: &Bound<'a, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for InternalUuid { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { let uuid_value = Uuid::parse_str(obj.str()?.extract::<&str>()?).map_err(|_| { RustPSQLDriverError::PyToRustValueConversionError( "Cannot convert UUID Array to inner rust type, check you parameters.".into(), diff --git a/src/value_converter/utils.rs b/src/value_converter/utils.rs index c94b2669..d7fea3b3 100644 --- a/src/value_converter/utils.rs +++ b/src/value_converter/utils.rs @@ -13,7 +13,7 @@ pub fn extract_value_from_python_object_or_raise<'py, T>( attr_name: &str, ) -> Result where - T: FromPyObject<'py>, + T: for<'a> FromPyObject<'a, 'py>, { parameter .getattr(attr_name) diff --git a/tox.ini b/tox.ini index 2127378a..fda2849c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] isolated_build = true env_list = + py315 py314 py313 py312 @@ -9,6 +10,7 @@ env_list = [gh] python = + 3.15 = py315 3.14 = py314 3.13 = py313 3.12 = py312 @@ -31,6 +33,21 @@ commands_pre = commands = pytest -vv +[testenv:py315] +skip_install = true +deps = + pytest>=7,<8 + pytest-timeout>=2,<3 + anyio>=3,<4 + maturin>=1,<2 + pydantic>=2 +allowlist_externals = maturin +commands_pre = + maturin develop +commands = + pytest -vv --ignore="./python/tests/test_binary_copy.py" + + [testenv:py314] skip_install = true deps =