Skip to content

Commit

Permalink
feat: allow adding routes without using decorator (#592)
Browse files Browse the repository at this point in the history
* feat: allow adding routes without using decorator

* fix: allow inserting string to add_route

* docs: add documentation for add_route

---------

Co-authored-by: Ido Slonimsky <[email protected]>
  • Loading branch information
IdoKendo and Ido Slonimsky authored Sep 7, 2023
1 parent 6b248b3 commit 21d9bbb
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 16 deletions.
12 changes: 12 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,18 @@ app.add_view("/", View)

```

## Route Registration

Instead of using the decorators, you can also add routes with a function:

```python
async def hello(request):
return "Hello World"

app.add_route("GET", "/hello", hello)
```

This works for all HTTP methods.

## Allow CORS

Expand Down
28 changes: 27 additions & 1 deletion integration_tests/base_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
from collections import defaultdict
from typing import Optional

from robyn import WS, Robyn, Request, Response, jsonify, serve_file, serve_html
from robyn import (
Request,
Response,
Robyn,
WS,
jsonify,
serve_file,
serve_html,
)
from robyn.authentication import AuthenticationHandler, BearerGetter, Identity
from robyn.templating import JinjaTemplate

Expand Down Expand Up @@ -714,6 +722,24 @@ async def async_auth(request: Request):
# ===== Main =====


def sync_without_decorator():
return "Success!"


async def async_without_decorator():
return "Success!"


app.add_route("GET", "/sync/get/no_dec", sync_without_decorator)
app.add_route("PUT", "/sync/put/no_dec", sync_without_decorator)
app.add_route("POST", "/sync/post/no_dec", sync_without_decorator)
app.add_route("GET", "/async/get/no_dec", async_without_decorator)
app.add_route("PUT", "/async/put/no_dec", async_without_decorator)
app.add_route("POST", "/async/post/no_dec", async_without_decorator)

# ===== Main =====


def main():
app.add_response_header("server", "robyn")
app.add_directory(
Expand Down
21 changes: 21 additions & 0 deletions integration_tests/test_add_route_without_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from collections.abc import Callable
import pytest
from integration_tests.helpers.http_methods_helpers import get, post, put


@pytest.mark.benchmark
@pytest.mark.usefixtures("session")
@pytest.mark.parametrize(
"route,method",
[
("/sync/get/no_dec", get),
("/async/get/no_dec", get),
("/sync/put/no_dec", put),
("/async/put/no_dec", put),
("/sync/post/no_dec", post),
("/async/post/no_dec", post),
],
)
def test_exception_handling(route: str, method: Callable):
r = method(route, expected_status_code=200)
assert r.text == "Success!"
48 changes: 33 additions & 15 deletions robyn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import multiprocess as mp
import os
from typing import Callable, List, Optional, Tuple
from typing import Callable, List, Optional, Tuple, Union
from nestd import get_all_nested


Expand All @@ -15,7 +15,14 @@
from robyn.logger import logger
from robyn.processpool import run_processes
from robyn.responses import serve_file, serve_html
from robyn.robyn import FunctionInfo, HttpMethod, Request, Response, get_version, jsonify
from robyn.robyn import (
FunctionInfo,
HttpMethod,
Request,
Response,
get_version,
jsonify,
)
from robyn.router import MiddlewareRouter, MiddlewareType, Router, WebSocketRouter
from robyn.types import Directory, Header
from robyn import status_codes
Expand Down Expand Up @@ -58,16 +65,16 @@ def __init__(self, file_object: str, config: Config = Config()) -> None:
self.exception_handler: Optional[Callable] = None
self.authentication_handler: Optional[AuthenticationHandler] = None

def _add_route(
def add_route(
self,
route_type: HttpMethod,
route_type: Union[HttpMethod, str],
endpoint: str,
handler: Callable,
is_const: bool = False,
auth_required: bool = False,
):
"""
This is base handler for all the route decorators
Connect a URI to a handler
:param route_type str: route type between GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS/TRACE
:param endpoint str: endpoint for the route added
Expand All @@ -80,6 +87,17 @@ def _add_route(
"""
if auth_required:
self.middleware_router.add_auth_middleware(endpoint)(handler)
if isinstance(route_type, str):
http_methods = {
"GET": HttpMethod.GET,
"POST": HttpMethod.POST,
"PUT": HttpMethod.PUT,
"DELETE": HttpMethod.DELETE,
"PATCH": HttpMethod.PATCH,
"HEAD": HttpMethod.HEAD,
"OPTIONS": HttpMethod.OPTIONS,
}
route_type = http_methods[route_type]

return self.router.add_route(
route_type, endpoint, handler, is_const, self.exception_handler
Expand Down Expand Up @@ -205,7 +223,7 @@ def get_functions(view) -> List[Tuple[HttpMethod, Callable]]:

handlers = get_functions(view)
for route_type, handler in handlers:
self._add_route(route_type, endpoint, handler, const)
self.add_route(route_type, endpoint, handler, const)

def view(self, endpoint: str, const: bool = False):
"""
Expand All @@ -227,7 +245,7 @@ def get(self, endpoint: str, const: bool = False, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.GET, endpoint, handler, const, auth_required
)

Expand All @@ -241,7 +259,7 @@ def post(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.POST, endpoint, handler, auth_required=auth_required
)

Expand All @@ -255,7 +273,7 @@ def put(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.PUT, endpoint, handler, auth_required=auth_required
)

Expand All @@ -269,7 +287,7 @@ def delete(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.DELETE, endpoint, handler, auth_required=auth_required
)

Expand All @@ -283,7 +301,7 @@ def patch(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.PATCH, endpoint, handler, auth_required=auth_required
)

Expand All @@ -297,7 +315,7 @@ def head(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.HEAD, endpoint, handler, auth_required=auth_required
)

Expand All @@ -311,7 +329,7 @@ def options(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.OPTIONS, endpoint, handler, auth_required=auth_required
)

Expand All @@ -325,7 +343,7 @@ def connect(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.CONNECT, endpoint, handler, auth_required=auth_required
)

Expand All @@ -339,7 +357,7 @@ def trace(self, endpoint: str, auth_required: bool = False):
"""

def inner(handler):
return self._add_route(
return self.add_route(
HttpMethod.TRACE, endpoint, handler, auth_required=auth_required
)

Expand Down

0 comments on commit 21d9bbb

Please sign in to comment.