Skip to content

Releases: sparckles/Robyn

v0.9.0 | WebSockets

Choose a tag to compare

@sansyrox sansyrox released this 03 Dec 14:30

Websocket Intergration

PR Links

Full Changelog: v0.8.0...v0.9.0

Robyn supports WebSockets now.

from robyn import Robyn, WS

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

@websocket.on("message")
def connect():
    global i
    i+=1
    if i==0:
        return "Whaaat??"
    elif i==1:
        return "Whooo??"
    elif i==2:
        i = -1
        return "*chika* *chika* Slim Shady."
        
@websocket.on("close")
def close():
    print("Hello world")
    return "Hello world, from ws"

@websocket.on("connect")
def message():
    print("Hello world")
    return "Hello world, from ws"
        

You can have 3 methods for every web socket:

"message", "close" and "connect" for responding to the message received, connection closed and connection initiated.

v0.8.1 (Minor Bug fix)

Choose a tag to compare

@sansyrox sansyrox released this 17 Nov 23:01

What's Changed

Full Changelog: v0.8.0...v0.8.1

app.start(url='127.0.0.1') no longer defaults to 0.0.0.0 and the defaults are also set to their original value.

v0.8.0

Choose a tag to compare

@sansyrox sansyrox released this 10 Nov 21:58

The latest version of Robyn now scales across multiple cores!!

PR LInks

Full Changelog: https://github.com/sansyrox/robyn/blob/main/CHANGELOG.md#v080-2021-11-10

You can select the number of processes and workers now:

python3 app.py --workers=5 --processes=5

Performance Comparison [CPU under very heavy strain]

Robyn - 5 Processes and 5 Workers (v0.8.0)

╰─ oha -n 500000 -c 50 http://localhost:5000/ --no-tui                                                                                                                                                                                                                                        ─╯
Summary:
  Success rate:	1.0000
  Total:	74.1781 secs
  Slowest:	0.1318 secs
  Fastest:	0.0005 secs
  Average:	0.0074 secs
  Requests/sec:	6740.5379

  Total data:	21.93 MiB
  Size/request:	46 B
  Size/sec:	302.80 KiB

Response time histogram:
  0.002 [49062]  |■■■■■■■■■■■■■
  0.004 [115710] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.006 [105245] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.008 [72753]  |■■■■■■■■■■■■■■■■■■■■
  0.010 [54286]  |■■■■■■■■■■■■■■■
  0.012 [36697]  |■■■■■■■■■■
  0.014 [22683]  |■■■■■■
  0.016 [14015]  |■■■
  0.018 [8920]   |■■
  0.020 [5815]   |■
  0.022 [14814]  |■■■■

Latency distribution:
  10% in 0.0025 secs
  25% in 0.0037 secs
  50% in 0.0060 secs
  75% in 0.0095 secs
  90% in 0.0136 secs
  95% in 0.0171 secs
  99% in 0.0278 secs

Details (average, fastest, slowest):
  DNS+dialup:	0.0026 secs, 0.0022 secs, 0.0031 secs
  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0001 secs

Status code distribution:
  [200] 500000 responses

Robyn - 1 Process and 1 Worker (v0.7.1)

╰─ oha -n 500000 -c 50 http://localhost:5000/ --no-tui                                                                                                                                                                                                                                        ─╯
Summary:
  Success rate:	1.0000
  Total:	101.4573 secs
  Slowest:	0.0858 secs
  Fastest:	0.0011 secs
  Average:	0.0101 secs
  Requests/sec:	4928.1797

  Total data:	8.95 MiB
  Size/request:	18 B
  Size/sec:	90.37 KiB

Response time histogram:
  0.003 [617]    |
  0.005 [23214]  |■■■
  0.008 [151605] |■■■■■■■■■■■■■■■■■■■■■
  0.011 [228487] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.013 [55292]  |■■■■■■■
  0.016 [32058]  |■■■■
  0.019 [7509]   |■
  0.021 [747]    |
  0.024 [153]    |
  0.027 [52]     |
  0.029 [266]    |

Latency distribution:
  10% in 0.0075 secs
  25% in 0.0086 secs
  50% in 0.0097 secs
  75% in 0.0111 secs
  90% in 0.0138 secs
  95% in 0.0155 secs
  99% in 0.0178 secs

Details (average, fastest, slowest):
  DNS+dialup:	0.0034 secs, 0.0019 secs, 0.0043 secs
  DNS-lookup:	0.0001 secs, 0.0000 secs, 0.0011 secs

Status code distribution:
  [200] 500000 responses

Conclusion

It saves around 27 seconds under high strain conditions. The default configs are at 1 worker and 1 process. You can config for whatever works best for you.

Contributors

Special thanks to @JackThomson2 @messense @awestlake87 for the help with the socket sharing feature! 🥳 ✨

Release v0.7.1 [Bug Fixes]

Choose a tag to compare

@sansyrox sansyrox released this 28 Oct 23:18

This version contains major fixes of the previous version:

  • Request objects are optional in every route handler now.
  • Robyn's test suite is now fixed. Now you can expect more reliable shipments!

example below

@app.get("/jsonify")
async def json_get():
    return jsonify({"hello": "world"})

v0.7.0

Choose a tag to compare

@sansyrox sansyrox released this 03 Oct 21:17

The latest version of Robyn supports route params!! 🥳

You can now add params in the routes and access them from the request object.

@app.post("/jsonify/:id")
async def json(request):
    print(request["params"]["id"])
    return jsonify({"hello": "world"})

v0.6.1

Choose a tag to compare

@sansyrox sansyrox released this 31 Aug 07:08

This new release contains some major developments.

  • Add the base of http requests #78 (sansyrox)
  • Add default port and a variable url #77 (sansyrox)
  • Make the request object accessible in every route #76 (sansyrox)
  • Add the basics for circle ci and testing framework #67 (sansyrox)
  • Update to pyo3 v0.14 #65 (sansyrox)
  • Add the static directory serving #64 (sansyrox)
  • Create a request object #61 (sansyrox)
  • Add the ability to add body in PUT, PATCH and DELETE #60 (sansyrox)
  • Implement a working dev server #40 (sansyrox)
  • Use Actix as base #35 (JackThomson2)

Test use:

from robyn import Robyn, static_file, jsonify
import asyncio

app = Robyn(__file__)

callCount = 0


@app.get("/")
async def h(request):
    print(request)
    global callCount
    callCount += 1
    message = "Called " + str(callCount) + " times"
    return message

@app.get("/test")
async def test():
    import os
    path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "index.html"))
    return static_file(path)

@app.post("/jsonify")
async def json(request):
    print(request)
    return jsonify({"hello": "world"})

@app.post("/post")
async def postreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.put("/put")
async def putreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.delete("/delete")
async def deletereq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.patch("/patch")
async def patchreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.get("/sleep")
async def sleeper():
    await asyncio.sleep(5)
    return "sleep function"


@app.get("/blocker")
def blocker():
    import time
    time.sleep(10)
    return "blocker function"


if __name__ == "__main__":
    app.add_header("server", "robyn")
    app.add_directory(route="/test_dir",directory_path="./test_dir/build", index_file="index.html")
    app.start(port=5000)

v0.6.0 Release | Major changes

Choose a tag to compare

@sansyrox sansyrox released this 11 Aug 08:18

This release is made up of a few major changes:

  • We have now updated to pyo3 0.14
  • We can now serve static directories(i.e. host a react project)
  • A request object is now available to PUT, POST and PATCH
  • You can now add body in PUT, PATCH and DELETE
  • We have a hot reloading server now looking for changes in directory
  • We are now using actix as a base

Special Thanks to:

Example usage:

from robyn import Robyn, static_file, jsonify

app = Robyn(__file__)

callCount = 0


@app.get("/")
async def h():
    global callCount
    callCount += 1
    message = "Called " + str(callCount) + " times"
    return message

@app.get("/test")
async def test():
    import os
    path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "index.html"))
    return static_file(path)

@app.post("/jsonify")
async def json(request):
    return jsonify({"hello": "world"})

@app.post("/post")
async def postreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.put("/put")
async def putreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.delete("/delete")
async def deletereq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.patch("/patch")
async def patchreq(request):
    return bytearray(request["body"]).decode("utf-8")

@app.get("/sleep")
async def sleeper():
    await asyncio.sleep(5)
    return "sleep function"


@app.get("/blocker")
def blocker():
    import time
    time.sleep(10)
    return "blocker function"


if __name__ == "__main__":
    app.add_header("server", "robyn")
    app.add_directory(route="/",directory_path="./test_dir/build", index_file="index.html")
    app.start(port=5000)

To enable hot reloading, use the following command:

python3 test.py --dev=true

Thank you for all the support during this release! Without the community, this would not be possible! ❤️ 🔥

Add support to serve static files.

Choose a tag to compare

@sansyrox sansyrox released this 10 Jul 20:33

Robyn can now serve static files and serve json response

from robyn import Robyn, static_file, jsonify
import asyncio

app = Robyn()

@app.get("/test")
async def test():
    import os
    path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "index.html"))
    return static_file(path)

@app.post("/jsonify")
async def json():
    return jsonify({"hello": "world"})

Robyn is now HTTP compliant

Choose a tag to compare

@sansyrox sansyrox released this 01 Jul 08:39

Robyn is now HTTP compliant and supports non blocking sync function.

Can process even 1million request without dropping. 🥳

➜  ~ oha -n 1000000 http://localhost:5000
Summary:
  Success rate:	1.0000
  Total:	426.8401 secs
  Slowest:	0.3269 secs
  Fastest:	0.0009 secs
  Average:	0.0213 secs
  Requests/sec:	2342.7976

  Total data:	17.17 MiB
  Size/request:	18 B
  Size/sec:	41.18 KiB

Response time histogram:
  0.006 [82867]  |■■■■■■■■■
  0.011 [272171] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.017 [56678]  |■■■■■■
  0.023 [123485] |■■■■■■■■■■■■■■
  0.029 [245641] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.034 [136749] |■■■■■■■■■■■■■■■■
  0.040 [28463]  |■■■
  0.046 [19679]  |■■
  0.052 [17939]  |■■
  0.057 [7251]   |
  0.063 [9077]   |■

Latency distribution:
  10% in 0.0070 secs
  25% in 0.0097 secs
  50% in 0.0229 secs
  75% in 0.0288 secs
  90% in 0.0340 secs
  95% in 0.0423 secs
  99% in 0.0570 secs

Details (average, fastest, slowest):
  DNS+dialup:	0.0039 secs, 0.0024 secs, 0.0049 secs
  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0006 secs

Status code distribution:
  [200] 1000000 responses

Support for Sync Functions

Choose a tag to compare

@sansyrox sansyrox released this 22 Jun 19:47

Robyn now supports non blocking (to some extent) sync functions.

You can now use def fx if your library is not await supported.

from robyn import Robyn
import asyncio

app = Robyn()

@app.get("/")
async def h():
    print("This is the message from coroutine")
    return "not sleep function"

@app.get("/sleep")
async def sleeper():
    await asyncio.sleep(5)
    return "sleep function"

@app.get("/blocker")
def blocker():
    import time
    time.sleep(10)
    return "blocker function"

app.start()