Skip to content

Commit 450cad1

Browse files
committed
fix: introduce PyRequest and PyResponse struct
1 parent 1ff3bb3 commit 450cad1

22 files changed

+411
-427
lines changed

Cargo.lock

+16-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ futures-util = "0.3.27"
3030
matchit = "0.7.0"
3131
socket2 = { version = "0.5.1", features = ["all"] }
3232
uuid = { version = "1.3.0", features = ["serde", "v4"] }
33-
serde = "1.0.155"
33+
serde = "1.0.157"
3434
serde_json = "1.0.94"
3535
log = "0.4.17"
3636

integration_tests/base_routes.py

+30-20
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,13 @@ def shutdown_handler():
5757

5858
@app.before_request("/sync/middlewares")
5959
def sync_before_request(request: Request):
60-
new_headers = request.headers
61-
new_headers["before"] = "sync_before_request"
62-
request.headers = new_headers
60+
request.headers["before"] = "sync_before_request"
6361
return request
6462

6563

6664
@app.after_request("/sync/middlewares")
6765
def sync_after_request(response: Response):
68-
new_headers = response.headers
69-
new_headers["after"] = "sync_after_request"
70-
response.headers = new_headers
66+
response.headers["after"] = "sync_after_request"
7167
response.body = response.body + " after"
7268
return response
7369

@@ -81,17 +77,13 @@ def sync_middlewares(request: Request):
8177

8278
@app.before_request("/async/middlewares")
8379
async def async_before_request(request: Request):
84-
new_headers = request.headers
85-
new_headers["before"] = "async_before_request"
86-
request.headers = new_headers
80+
request.headers["before"] = "async_before_request"
8781
return request
8882

8983

9084
@app.after_request("/async/middlewares")
9185
async def async_after_request(response: Response):
92-
new_headers = response.headers
93-
new_headers["after"] = "async_after_request"
94-
response.headers = new_headers
86+
response.headers["after"] = "async_after_request"
9587
response.body = response.body + " after"
9688
return response
9789

@@ -274,28 +266,46 @@ async def async_param(request: Request):
274266

275267

276268
@app.get("/sync/extra/*extra")
277-
def sync_param_extra(request):
278-
extra = request["params"]["extra"]
269+
def sync_param_extra(request: Request):
270+
extra = request.params["extra"]
279271
return extra
280272

281273

282274
@app.get("/async/extra/*extra")
283-
async def async_param_extra(request):
284-
extra = request["params"]["extra"]
275+
async def async_param_extra(request: Request):
276+
extra = request.params["extra"]
285277
return extra
286278

287279

288280
# Request Info
289281

290282

291283
@app.get("/sync/http/param")
292-
def sync_http_param(request):
293-
return jsonify({"url": request["url"], "method": request["method"]})
284+
def sync_http_param(request: Request):
285+
return jsonify(
286+
{
287+
"url": {
288+
"scheme": request.url.scheme,
289+
"host": request.url.host,
290+
"path": request.url.path,
291+
},
292+
"method": request.method,
293+
}
294+
)
294295

295296

296297
@app.get("/async/http/param")
297-
async def async_http_param(request):
298-
return jsonify({"url": request["url"], "method": request["method"]})
298+
async def async_http_param(request: Request):
299+
return jsonify(
300+
{
301+
"url": {
302+
"scheme": request.url.scheme,
303+
"host": request.url.host,
304+
"path": request.url.path,
305+
},
306+
"method": request.method,
307+
}
308+
)
299309

300310

301311
# HTML serving

integration_tests/test_middlewares.py

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
@pytest.mark.parametrize("function_type", ["sync", "async"])
88
def test_middlewares(function_type: str, session):
99
r = get(f"/{function_type}/middlewares")
10+
# We do not want the request headers to be in the response
11+
assert "before" not in r.headers
1012
assert "after" in r.headers
1113
assert r.headers["after"] == f"{function_type}_after_request"
1214
assert r.text == f"{function_type} middlewares after"

integration_tests/views/async_view.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,4 @@ async def get():
66
return "Hello, world!"
77

88
async def post(request: Request):
9-
body = request.body
10-
return {
11-
"status": 200,
12-
"body": body,
13-
"headers": {"Content-Type": "text/json"},
14-
}
9+
return request.body

integration_tests/views/sync_view.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,4 @@ def get():
66
return "Hello, world!"
77

88
def post(request: Request):
9-
body = request.body
10-
return {
11-
"status": 200,
12-
"body": body,
13-
"headers": {"Content-Type": "text/json"},
14-
}
9+
return request.body

robyn/robyn.pyi

+10-13
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,27 @@ class FunctionInfo:
1515
is_async: bool
1616
number_of_params: int
1717

18-
@dataclass
19-
class Body:
20-
content: Union[str, bytes]
21-
22-
def as_bytes(self) -> bytes:
23-
pass
24-
def set(self, content: Union[str, bytes]):
25-
pass
18+
class Url:
19+
scheme: str
20+
host: str
21+
path: str
2622

2723
@dataclass
2824
class Request:
2925
queries: dict[str, str]
3026
headers: dict[str, str]
3127
params: dict[str, str]
32-
body: Body
28+
body: Union[str, bytes]
29+
method: str
30+
url: Url
3331

3432
@dataclass
3533
class Response:
3634
status_code: int
35+
response_type: str
3736
headers: dict[str, str]
38-
body: Body
39-
40-
def set_file_path(self, file_path: str):
41-
pass
37+
body: Union[str, bytes]
38+
file_path: str
4239

4340
class Server:
4441
def __init__(self) -> None:

robyn/router.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def _format_response(self, res):
3737
response = Response(status_code=status_code, headers=headers, body=body)
3838
file_path = res.get("file_path")
3939
if file_path is not None:
40-
response.set_file_path(file_path)
40+
response.file_path = file_path
4141
elif isinstance(res, Response):
4242
response = res
4343
elif isinstance(res, bytes):

src/executors/mod.rs

+21-19
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,56 @@
11
/// This is the module that has all the executor functions
22
/// i.e. the functions that have the responsibility of parsing and executing functions.
3-
use crate::types::{FunctionInfo, Request, Response};
4-
53
use std::sync::Arc;
64

7-
use anyhow::{anyhow, Context, Result};
5+
use anyhow::{Context, Result};
86
use log::debug;
7+
use pyo3::prelude::*;
98
use pyo3_asyncio::TaskLocals;
10-
// pyO3 module
11-
use pyo3::{prelude::*, PyClass};
9+
10+
use crate::types::{function_info::FunctionInfo, request::Request, response::Response};
1211

1312
fn get_function_output<'a, T>(
1413
function: &'a FunctionInfo,
1514
py: Python<'a>,
1615
input: &T,
1716
) -> Result<&'a PyAny, PyErr>
1817
where
19-
T: Clone + IntoPy<Py<PyAny>>,
18+
T: ToPyObject,
2019
{
2120
let handler = function.handler.as_ref(py);
2221

2322
// this makes the request object accessible across every route
2423
match function.number_of_params {
2524
0 => handler.call0(),
26-
1 => handler.call1((input.clone(),)),
25+
1 => handler.call1((input.to_object(py),)),
2726
// this is done to accommodate any future params
28-
2_u8..=u8::MAX => handler.call1((input.clone(),)),
27+
2_u8..=u8::MAX => handler.call1((input.to_object(py),)),
2928
}
3029
}
3130

32-
pub async fn execute_middleware_function<'a, T>(input: &T, function: FunctionInfo) -> Result<T>
31+
pub async fn execute_middleware_function<T>(input: &T, function: FunctionInfo) -> Result<T>
3332
where
34-
T: Clone + PyClass + IntoPy<Py<PyAny>>,
33+
T: for<'a> FromPyObject<'a> + ToPyObject,
3534
{
3635
if function.is_async {
37-
let output = Python::with_gil(|py| {
36+
let output: Py<PyAny> = Python::with_gil(|py| {
3837
pyo3_asyncio::tokio::into_future(get_function_output(&function, py, input)?)
3938
})?
4039
.await?;
4140

42-
Python::with_gil(|py| -> PyResult<T> {
43-
let output: (T,) = output.extract(py)?;
41+
Python::with_gil(|py| -> Result<T> {
42+
let output: (T,) = output
43+
.extract(py)
44+
.context("Failed to get middleware response")?;
4445
Ok(output.0)
4546
})
46-
.map_err(|e| anyhow!(e))
4747
} else {
48-
Python::with_gil(|py| -> PyResult<T> {
49-
let output: (T,) = get_function_output(&function, py, input)?.extract()?;
48+
Python::with_gil(|py| -> Result<T> {
49+
let output: (T,) = get_function_output(&function, py, input)?
50+
.extract()
51+
.context("Failed to get middleware response")?;
5052
Ok(output.0)
5153
})
52-
.map_err(|e| anyhow!(e))
5354
}
5455
}
5556

@@ -67,8 +68,9 @@ pub async fn execute_http_function(request: &Request, function: FunctionInfo) ->
6768
})
6869
} else {
6970
Python::with_gil(|py| -> Result<Response> {
70-
let output = get_function_output(&function, py, request)?;
71-
output.extract().context("Failed to get route response")
71+
get_function_output(&function, py, request)?
72+
.extract()
73+
.context("Failed to get route response")
7274
})
7375
}
7476
}

src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ use shared_socket::SocketHeld;
1111

1212
// pyO3 module
1313
use pyo3::prelude::*;
14-
use types::{ActixBytesWrapper, FunctionInfo, Request, Response};
14+
use types::{function_info::FunctionInfo, request::PyRequest, response::PyResponse, Url};
1515

1616
#[pymodule]
1717
pub fn robyn(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
1818
// the pymodule class to make the rustPyFunctions available
1919
m.add_class::<Server>()?;
2020
m.add_class::<SocketHeld>()?;
2121
m.add_class::<FunctionInfo>()?;
22-
m.add_class::<Request>()?;
23-
m.add_class::<Response>()?;
24-
m.add_class::<ActixBytesWrapper>()?;
22+
m.add_class::<PyRequest>()?;
23+
m.add_class::<PyResponse>()?;
24+
m.add_class::<Url>()?;
2525
pyo3::prepare_freethreaded_python();
2626
Ok(())
2727
}

src/routers/const_router.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use std::sync::Arc;
44
use std::sync::RwLock;
55

66
use crate::executors::execute_http_function;
7-
use crate::types::Response;
8-
use crate::types::{FunctionInfo, Request};
7+
use crate::types::function_info::FunctionInfo;
8+
use crate::types::request::Request;
9+
use crate::types::response::Response;
910
use anyhow::Context;
1011
use log::debug;
1112
use matchit::Router as MatchItRouter;

0 commit comments

Comments
 (0)