Skip to content

Commit ccfb40b

Browse files
authored
feat: optimize robyn , performance boost by 80% (#1180)
* feat: optimize robyn * fix: formatting * fix comment
1 parent 571a584 commit ccfb40b

File tree

3 files changed

+64
-54
lines changed

3 files changed

+64
-54
lines changed

src/executors/mod.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,26 @@ where
7272
.await?;
7373

7474
Python::with_gil(|py| -> Result<MiddlewareReturn> {
75-
let output_response = output.extract::<Response>(py);
76-
match output_response {
77-
Ok(o) => Ok(MiddlewareReturn::Response(o)),
78-
Err(_) => Ok(MiddlewareReturn::Request(output.extract::<Request>(py)?)),
75+
// Try response extraction first, then request
76+
match output.extract::<Response>(py) {
77+
Ok(response) => Ok(MiddlewareReturn::Response(response)),
78+
Err(_) => match output.extract::<Request>(py) {
79+
Ok(request) => Ok(MiddlewareReturn::Request(request)),
80+
Err(e) => Err(e.into()),
81+
},
7982
}
8083
})
8184
} else {
8285
Python::with_gil(|py| -> Result<MiddlewareReturn> {
8386
let output = get_function_output(function, py, input)?;
8487
debug!("Middleware output: {:?}", output);
88+
8589
match output.extract::<Response>() {
86-
Ok(o) => Ok(MiddlewareReturn::Response(o)),
87-
Err(_) => Ok(MiddlewareReturn::Request(output.extract::<Request>()?)),
90+
Ok(response) => Ok(MiddlewareReturn::Response(response)),
91+
Err(_) => match output.extract::<Request>() {
92+
Ok(request) => Ok(MiddlewareReturn::Request(request)),
93+
Err(e) => Err(e.into()),
94+
},
8895
}
8996
})
9097
}
@@ -102,12 +109,12 @@ pub async fn execute_http_function(
102109
})?
103110
.await?;
104111

105-
return Python::with_gil(|py| -> PyResult<Response> { output.extract(py) });
106-
};
107-
108-
Python::with_gil(|py| -> PyResult<Response> {
109-
get_function_output(function, py, request)?.extract()
110-
})
112+
Python::with_gil(|py| -> PyResult<Response> { output.extract(py) })
113+
} else {
114+
Python::with_gil(|py| -> PyResult<Response> {
115+
get_function_output(function, py, request)?.extract()
116+
})
117+
}
111118
}
112119

113120
pub async fn execute_startup_handler(

src/types/request.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,45 +35,51 @@ impl<'py> IntoPyObject<'py> for Request {
3535
type Output = Bound<'py, Self::Target>;
3636
type Error = PyErr;
3737
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
38-
let query_params = self.query_params.clone();
39-
let headers: Py<Headers> = self.headers.clone().into_pyobject(py)?.extract()?;
40-
let path_params = self.path_params.clone().into_pyobject(py)?.extract()?;
41-
let body = match String::from_utf8(self.body.clone()) {
42-
Ok(s) => s.into_pyobject(py)?.into_any(),
43-
Err(_) => self.body.clone().into_pyobject(py)?.into_any(),
38+
let headers: Py<Headers> = self.headers.into_pyobject(py)?.extract()?;
39+
let path_params = self.path_params.into_pyobject(py)?.extract()?;
40+
41+
let body = if self.body.is_empty() {
42+
PyString::new(py, "").into_any()
43+
} else {
44+
match String::from_utf8(self.body.clone()) {
45+
Ok(s) => s.into_pyobject(py)?.into_any(),
46+
Err(_) => self.body.into_pyobject(py)?.into_any(),
47+
}
4448
};
45-
let form_data: Py<PyDict> = match &self.form_data {
46-
Some(data) => {
49+
50+
let form_data: Py<PyDict> = match self.form_data {
51+
Some(data) if !data.is_empty() => {
4752
let dict = PyDict::new(py);
48-
for (key, value) in data.iter() {
49-
dict.set_item(key, value).unwrap();
53+
// Use with_capacity equivalent by setting items directly
54+
for (key, value) in data {
55+
dict.set_item(key, value)?;
5056
}
5157
dict.into()
5258
}
53-
None => PyDict::new(py).into(),
59+
_ => PyDict::new(py).into(),
5460
};
5561

56-
let files: Py<PyDict> = match &self.files {
57-
Some(data) => {
62+
let files: Py<PyDict> = match self.files {
63+
Some(data) if !data.is_empty() => {
5864
let dict = PyDict::new(py);
59-
for (key, value) in data.iter() {
60-
let bytes = PyBytes::new(py, value);
61-
dict.set_item(key, bytes).unwrap();
65+
for (key, value) in data {
66+
let bytes = PyBytes::new(py, &value);
67+
dict.set_item(key, bytes)?;
6268
}
6369
dict.into()
6470
}
65-
None => PyDict::new(py).into(),
71+
_ => PyDict::new(py).into(),
6672
};
6773

6874
let request = PyRequest {
69-
query_params,
75+
query_params: self.query_params,
7076
path_params,
7177
headers,
7278
body: body.into(),
73-
method: self.method.clone(),
74-
url: self.url.clone(),
75-
ip_addr: self.ip_addr.clone(),
76-
identity: self.identity.clone(),
79+
method: self.method,
80+
url: self.url,
81+
ip_addr: self.ip_addr,
82+
identity: self.identity,
7783
form_data,
7884
files,
7985
};

src/types/response.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,25 @@ impl Responder for Response {
3636

3737
impl Response {
3838
pub fn not_found(headers: Option<&Headers>) -> Self {
39-
let headers = match headers {
40-
Some(headers) => headers.clone(),
41-
None => Headers::new(None),
42-
};
39+
const NOT_FOUND_BYTES: &[u8] = b"Not found";
4340

4441
Self {
4542
status_code: 404,
4643
response_type: "text".to_string(),
47-
headers,
48-
description: "Not found".to_owned().into_bytes(),
44+
headers: headers.cloned().unwrap_or_else(|| Headers::new(None)),
45+
description: NOT_FOUND_BYTES.to_vec(),
4946
file_path: None,
5047
}
5148
}
5249

5350
pub fn internal_server_error(headers: Option<&Headers>) -> Self {
54-
let headers = match headers {
55-
Some(headers) => headers.clone(),
56-
None => Headers::new(None),
57-
};
51+
const SERVER_ERROR_BYTES: &[u8] = b"Internal server error";
5852

5953
Self {
6054
status_code: 500,
6155
response_type: "text".to_string(),
62-
headers,
63-
description: "Internal server error".to_owned().into_bytes(),
56+
headers: headers.cloned().unwrap_or_else(|| Headers::new(None)),
57+
description: SERVER_ERROR_BYTES.to_vec(),
6458
file_path: None,
6559
}
6660
}
@@ -71,20 +65,23 @@ impl<'py> IntoPyObject<'py> for Response {
7165
type Output = Bound<'py, Self::Target>;
7266
type Error = PyErr;
7367
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
74-
let headers = self.headers.clone().into_pyobject(py)?.extract()?;
75-
// The description should only be either string or binary.
76-
// it should raise an exception otherwise
77-
let description = match String::from_utf8(self.description.to_vec()) {
78-
Ok(description) => description.into_pyobject(py)?.into_any(),
79-
Err(_) => PyBytes::new(py, &self.description.to_vec()).into_any(),
68+
let headers = self.headers.into_pyobject(py)?.extract()?;
69+
70+
let description = if self.description.is_empty() {
71+
"".into_pyobject(py)?.into_any()
72+
} else {
73+
match String::from_utf8(self.description.clone()) {
74+
Ok(description) => description.into_pyobject(py)?.into_any(),
75+
Err(_) => PyBytes::new(py, &self.description).into_any(),
76+
}
8077
};
8178

8279
let response = PyResponse {
8380
status_code: self.status_code,
84-
response_type: self.response_type.clone(),
81+
response_type: self.response_type,
8582
headers,
8683
description: description.into(),
87-
file_path: self.file_path.clone(),
84+
file_path: self.file_path,
8885
};
8986
Ok(Py::new(py, response)?.into_bound(py).into_any())
9087
}

0 commit comments

Comments
 (0)