Skip to content

Releases: sparckles/Robyn

v0.9.0 | WebSockets

03 Dec 14:30
Compare
Choose a tag to compare

Websocket Intergration

PR Links

Full Changelog: https://github.com/sansyrox/robyn/compare/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)

17 Nov 23:01
Compare
Choose a tag to compare

What's Changed

Full Changelog: https://github.com/sansyrox/robyn/compare/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

10 Nov 21:58
Compare
Choose a tag to compare

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]

28 Oct 23:18
Compare
Choose a tag to compare

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

03 Oct 21:17
Compare
Choose a tag to compare

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

31 Aug 07:08
Compare
Choose a tag to compare

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

11 Aug 08:18
Compare
Choose a tag to compare

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.

10 Jul 20:33
Compare
Choose a tag to compare

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

01 Jul 08:39
Compare
Choose a tag to compare

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

22 Jun 19:47
Compare
Choose a tag to compare

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()