From a6cff1cc33a49e646a78be5473c19f1c6a2f4f67 Mon Sep 17 00:00:00 2001 From: deedy5 <65482418+deedy5@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:59:27 +0300 Subject: [PATCH] Improve `json` handling: add static JSON_DUMPS and JSON_LOADS --- src/lib.rs | 8 ++++--- src/response.rs | 8 +++---- src/utils.rs | 64 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 045a499..0b21fbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,9 +246,11 @@ impl Client { .map(|data_pydict| url_encode(py, Some(data_pydict)).ok()) .unwrap_or_else(|| None); // Converts 'json' (if any) into a JSON string for sending the data as `application/json` content type. - let json_str = json - .map(|json_pydict| json_dumps(py, Some(json_pydict)).ok()) - .unwrap_or_else(|| None); + let json_str = json + .map(|json_pydict| { + json_dumps(py).call1((json_pydict.clone().unbind(),)).unwrap().extract::().ok() + }) + .unwrap_or(None); let future = async move { // Check if method is POST || PUT || PATCH diff --git a/src/response.rs b/src/response.rs index 784e960..8f4b9b1 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_encoding_from_content, get_encoding_from_headers}; +use crate::utils::{get_encoding_from_content, get_encoding_from_headers, json_loads}; use ahash::RandomState; use anyhow::{anyhow, Result}; use encoding_rs::Encoding; @@ -78,9 +78,9 @@ impl Response { } fn json(&mut self, py: Python) -> Result { - let json_module = PyModule::import_bound(py, "json")?; - let loads = json_module.getattr("loads")?; - let result = loads.call1((&self.content,))?.extract::()?; + let result = json_loads(py) + .call1((&self.content,))? + .extract::()?; Ok(result) } diff --git a/src/utils.rs b/src/utils.rs index bf8481a..796e71a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,8 +3,50 @@ use std::cmp::min; use ahash::RandomState; use indexmap::IndexMap; use pyo3::prelude::*; +use pyo3::sync::GILOnceCell; use pyo3::types::{PyBool, PyDict}; +static JSON_DUMPS: GILOnceCell> = GILOnceCell::new(); +static JSON_LOADS: GILOnceCell> = GILOnceCell::new(); + +/// python json.dumps +pub fn json_dumps(py: Python<'_>) -> &Bound<'_, PyAny> { + JSON_DUMPS + .get_or_init(py, || { + py.import_bound("json") + .unwrap() + .getattr("dumps") + .unwrap() + .unbind() + }) + .bind(py) +} + +/// python json.loads +pub fn json_loads(py: Python<'_>) -> &Bound<'_, PyAny> { + JSON_LOADS + .get_or_init(py, || { + py.import_bound("json") + .unwrap() + .getattr("loads") + .unwrap() + .unbind() + }) + .bind(py) +} + +/// python urllib.parse.urlencode +pub fn url_encode(py: Python, pydict: Option<&Bound<'_, PyDict>>) -> PyResult { + let urllib_parse = PyModule::import_bound(py, "urllib.parse")?; + let urlencode = urllib_parse.getattr("urlencode")?; + match pydict { + Some(dict) => urlencode + .call1((dict, ("doseq", py.get_type_bound::().call1(())?)))? + .extract::(), + None => Ok("".to_string()), + } +} + /// Get encoding from the "Content-Type" header pub fn get_encoding_from_headers( headers: &IndexMap, @@ -56,28 +98,6 @@ pub fn get_encoding_from_content(raw_bytes: &[u8]) -> Option { } } -/// python json.dumps -pub fn json_dumps(py: Python, pydict: Option<&Bound<'_, PyDict>>) -> PyResult { - let json_module = PyModule::import_bound(py, "json")?; - let dumps = json_module.getattr("dumps")?; - match pydict { - Some(dict) => dumps.call1((dict,))?.extract::(), - None => Ok("".to_string()), - } -} - -/// python urllib.parse.urlencode -pub fn url_encode(py: Python, pydict: Option<&Bound<'_, PyDict>>) -> PyResult { - let urllib_parse = PyModule::import_bound(py, "urllib.parse")?; - let urlencode = urllib_parse.getattr("urlencode")?; - match pydict { - Some(dict) => urlencode - .call1((dict, ("doseq", py.get_type_bound::().call1(())?)))? - .extract::(), - None => Ok("".to_string()), - } -} - #[cfg(test)] mod utils_tests { use super::*;