Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add view controllers #407

Merged
merged 8 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
pip install --no-index --find-links=dist/ robyn
- name: Test with pytest
run: |
pytest ./integration_tests
pytest
27 changes: 27 additions & 0 deletions integration_tests/base_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from robyn.robyn import Response
from robyn.templating import JinjaTemplate

from views import SyncView, AsyncView

app = Robyn(__file__)
websocket = WS(app, "/web_socket")

Expand Down Expand Up @@ -482,6 +484,29 @@ async def async_raise():
raise Exception()


# ===== Views =====


@app.view("/sync/view/decorator")
def sync_decorator_view():
def get():
return "Hello, world!"

def post(request):
body = bytearray(request["body"]).decode("utf-8")
return {"status_code": 200, "body": body}


@app.view("/async/view/decorator")
def async_decorator_view():
async def get():
return "Hello, world!"

async def post(request):
body = bytearray(request["body"]).decode("utf-8")
return {"status_code": 200, "body": body}


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


Expand All @@ -493,4 +518,6 @@ async def async_raise():
index_file="index.html",
)
app.startup_handler(startup_handler)
app.add_view("/sync/view", SyncView)
app.add_view("/async/view", AsyncView)
app.start(port=8080)
41 changes: 41 additions & 0 deletions integration_tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from helpers.http_methods_helpers import get, post


def test_get_sync_view(session):
r = get("/sync/view")
assert r.text == "Hello, world!"


def test_post_sync_view(session):
r = post("/sync/view", data={"name": "John"})
assert "John" in r.text


def test_get_sync_decorator_view(session):
r = get("/sync/view/decorator")
assert r.text == "Hello, world!"


def test_post_sync_decorator_view(session):
r = post("/sync/view/decorator", data={"name": "John"})
assert "John" in r.text


def test_get_async_view(session):
r = get("/async/view")
assert r.text == "Hello, world!"


def test_post_async_view(session):
r = post("/async/view", data={"name": "John"})
assert "John" in r.text


def test_get_async_decorator_view(session):
r = get("/async/view/decorator")
assert r.text == "Hello, world!"


def test_post_async_decorator_view(session):
r = post("/async/view/decorator", data={"name": "John"})
assert "John" in r.text
4 changes: 4 additions & 0 deletions integration_tests/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .sync_view import SyncView
from .async_view import AsyncView

__all__ = ["SyncView", "AsyncView"]
11 changes: 11 additions & 0 deletions integration_tests/views/async_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def AsyncView():
sansyrox marked this conversation as resolved.
Show resolved Hide resolved
async def get():
return "Hello, world!"

async def post(request):
body = bytes(request["body"]).decode("utf-8")
return {
"status": 200,
"body": body,
"headers": {"Content-Type": "text/json"},
}
11 changes: 11 additions & 0 deletions integration_tests/views/sync_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def SyncView():
def get():
return "Hello, world!"

def post(request):
body = bytes(request["body"]).decode("utf-8")
return {
"status": 200,
"body": body,
"headers": {"Content-Type": "text/json"},
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ name = "robyn"
dependencies = [
'watchdog == 2.2.1',
'multiprocess == 0.70.14',
'nestd==0.3.1',
# conditional
'uvloop == 0.17.0; sys_platform == "darwin"',
'uvloop == 0.17.0; platform_machine == "x86_64"',
Expand Down Expand Up @@ -43,7 +44,6 @@ Changelog = "https://github.com/sansyrox/robyn/blob/main/CHANGELOG.md"
[project.optional-dependencies]
"templating" = ["jinja2 == 3.0.1"]


[tool.ruff]
line-length = 160
exclude = ["src/*" , ".git" , "docs"]
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ uvloop; platform_system!="Windows"
watchdog==2.2.1
multiprocess==0.70.14
jinja2==3.1.2
nestd==0.3.1
43 changes: 39 additions & 4 deletions robyn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import signal
from typing import Callable, List, Optional
from nestd import get_all_nested

from watchdog.observers import Observer

Expand Down Expand Up @@ -39,11 +40,11 @@ def __init__(self, file_object: str) -> None:

def _add_route(self, route_type, endpoint, handler, is_const=False):
"""
[This is base handler for all the decorators]
This is base handler for all the decorators

:param route_type [str]: [route type between GET/POST/PUT/DELETE/PATCH]
:param endpoint [str]: [endpoint for the route added]
:param handler [function]: [represents the sync or async function passed as a handler for the route]
:param route_type str: route type between GET/POST/PUT/DELETE/PATCH
:param endpoint str: endpoint for the route added
:param handler function: represents the sync or async function passed as a handler for the route
"""

""" We will add the status code here only
Expand Down Expand Up @@ -165,6 +166,40 @@ def terminating_signal_handler(_sig, _frame):
observer.stop()
observer.join()

def add_view(self, endpoint: str, view: Callable, const: bool = False):
"""
This is base handler for the view decorators

:param endpoint str: endpoint for the route added
:param handler function: represents the function passed as a parent handler for single route with different route types
"""
http_methods = {"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"}
AntoineRR marked this conversation as resolved.
Show resolved Hide resolved

def get_functions(view):
functions = get_all_nested(view)
output = []
for name, handler in functions:
route_type = name.upper()
if route_type in http_methods:
output.append((route_type, handler))
return output

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

def view(self, endpoint: str, const: bool = False):
"""
The @app.view decorator to add a view with the GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS method

:param endpoint str: endpoint to server the route
"""

def inner(handler):
return self.add_view(endpoint, handler, const)

return inner

def get(self, endpoint: str, const: bool = False):
"""
The @app.get decorator to add a route with the GET method
Expand Down