# Fastipy
<div>
<img src="https://i.imgur.com/KCi8IUS.png">
</div>
[![CI](https://github.com/Bielgomes/fastipy/actions/workflows/pipeline.yaml/badge.svg)](https://github.com/Bielgomes/fastipy/actions/workflows/pipeline.yaml)
[![codecov](https://codecov.io/gh/Bielgomes/fastipy/graph/badge.svg?token=AF45LFYAP2)](https://codecov.io/gh/Bielgomes/fastipy)
[![PyPI version](https://badge.fury.io/py/fastipy.svg)](https://badge.fury.io/py/fastipy)
## What is it and what is it for
[Fastipy](https://pypi.org/project/Fastipy/) is a fast and easy-to-use open source Python library for developing RESTful APIs.
Powered by **[uvicorn](https://www.uvicorn.org/)**
## Installation
```bash
pip install fastipy
```
## Examples
### Example for GET Route with Query Params
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
# Routes can be async or sync functions, but reply send functions are async
# The handler returns the default HTTP status code 200
@app.get("/")
def home(req: Request, _):
# Get query params age
age = req.query["age"]
# Example: Recovery all persons from database with this age and print the html
print("<h1>Retrieving all persons</h1><ul><li>A Person</li></ul>")
```
### Example for GET Route with Params, CORS and multiple methods
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy().cors()
@app.get("/user/:id")
@app.post("/user/:id")
async def getUser(req: Request, reply: Reply):
# get users from database
for i in users:
if i["id"] == req.params["id"]:
# All response functions are asynchronous
return await reply.send(i)
await reply.send_code(404)
```
### Example for POST Route with Body
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
@app.post("/user")
async def createUser(req: Request, reply: Reply):
user = req.body.json
# Save user in database
await reply.code(201).send("Created")
```
### Example for PUT Route with Body
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
@app.put("/user")
async def createUser(req: Request, reply: Reply):
user = req.body.json
# Update user in database
await reply.type("text/html").code(201).send("<h1>Created</h1>")
```
### Example for GET Route with file stream
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
@app.get("/stream")
async def streamFile(_, reply: Reply):
# It could be an asynchronous generator
def generator():
with open("file.txt") as f:
for line in f:
yield line
await reply.send(generator())
```
### Adding custom serializer to Reply send
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
app.add_serializer(
validation=lambda data: isinstance(data, str),
serializer=lambda data: ("application/json", json.dumps({"error": data})),
)
@app.get("/")
async def customSerializer(_, reply: Reply):
await reply.code(404).send("Field not found")
```
### Running
Running Fastipy application in development is easy
```py
import uvicorn
if __name__ == "__main__":
# main:app indicates the FILE:VARIABLE
# The file is the main file where Fastipy() is instantiated
# The variable is the name of the variable that contains the instance of Fastipy()
# You can find more configurations here https://www.uvicorn.org/
# set reload to True for automatic reloading!
uvicorn.run("main:app", log_level="debug", port=8000, reload=True, loop="asyncio")
```
### See more examples in **[examples](https://github.com/Bielgomes/Fastipy/tree/main/examples)** folder
## Creating plugins
```py
# chat.py
from fastipy import FastipyInstance, Reply
# Plugins can be asynchronous or synchronized functions
# Plugins have access to the main instance, which means they can use all of Fastipy's functions
def chatRoutes(app: FastipyInstance, options: dict):
@app.get("/")
async def index(_, reply: Reply):
await reply.send_code(200)
@app.get("/chat")
async def test(_, reply: Reply):
await reply.send_code(200)
```
```py
# message.py
from fastipy import FastipyInstance, Reply
async def messageRoutes(app: FastipyInstance, options: dict):
@message.get("/")
async def index(_, reply: Reply):
await reply.send_code(200)
@message.get("/message")
async def test(_, reply: Reply):
await reply.send_code(200)
app.name("custom plugin name")
```
```py
# main.py
from fastipy import Fastipy
from message import messageRoutes
from chat import chatRoutes
app = Fastipy().cors()
app.register(messageRoutes, {"prefix": "/message"})
app.register(chatRoutes, {"prefix": "/chat"})
```
## Hooks
```py
from fastipy import Fastipy, Request, Reply
app = Fastipy()
# The preHandler hook is called before the request handler
@app.hook("preHandler")
def preHandler(req: Request, reply: Reply):
print("onRequest hook")
# The onRequest hook is called when the request is handled
@app.hook("onRequest")
def onRequest(req: Request, reply: Reply):
print("onRequest hook")
# The onResponse hook is called when the reply sends a response
@app.hook("onResponse")
def onResponse(req: Request, reply: Reply):
print("onResponse hook")
# The onError hook is called when an error occurs
@app.hook("onError")
def onError(error: Exception, req: Request, reply: Reply):
print(f"onError hook exception: {error}")
# A hook will only be linked to a route if its declaration precedes the route
# The order of hooks of the same type is important
@app.get("/")
async def index(_, reply: Reply):
await reply.send_code(200)
```
## End to End tests
```py
# See more in https://www.starlette.io/testclient/
from fastipy import TestClient
from main import app
client = TestClient(app)
response = client.post("/")
assert response.status_code == 200
assert response.text == "Hello World"
```
# Application Deploy
For production deployment, please refer to this **[uvicorn guide](https://www.uvicorn.org/deployment/)**.
# Change Log
## Development Version 1.5.4
### Todo
### Added
- [X] CI with Github Actions.
- [X] Code Coverage with Codecov.
### Changed
- [X] Change the routes handler to use the Raw Path instead of the Path (This Change allow the use of %20 %2F and other special characters in the path).
- [X] Improve functionality to handle asynchronous functions in synchronous environments (This change probably fix Memory Leak in the Fastipy).
### Fixed
- [X] Fix the bug in the search route where special characters are not being URL-escaped.
# Contributors
<a href="https://github.com/Bielgomes/Fastipy/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Bielgomes/Fastipy"/>
</a>
## How to Contributing
Open pull request
Raw data
{
"_id": null,
"home_page": "https://github.com/Bielgomes/Fastipy",
"name": "fastipy",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0.0,>=3.10.11",
"maintainer_email": null,
"keywords": "RESTful, API, ASGI, FastAPI, Fastipy, web server",
"author": "Bielgomes",
"author_email": "bielgomesdasilva@hotmail.com",
"download_url": "https://files.pythonhosted.org/packages/aa/45/ba82180bff27c32ed5225eac7c1d7743cd8aecf4ade12396984b9d26396c/fastipy-1.5.4.tar.gz",
"platform": null,
"description": "# Fastipy\n\n<div>\n <img src=\"https://i.imgur.com/KCi8IUS.png\">\n</div>\n\n[![CI](https://github.com/Bielgomes/fastipy/actions/workflows/pipeline.yaml/badge.svg)](https://github.com/Bielgomes/fastipy/actions/workflows/pipeline.yaml)\n[![codecov](https://codecov.io/gh/Bielgomes/fastipy/graph/badge.svg?token=AF45LFYAP2)](https://codecov.io/gh/Bielgomes/fastipy)\n[![PyPI version](https://badge.fury.io/py/fastipy.svg)](https://badge.fury.io/py/fastipy)\n\n## What is it and what is it for\n\n[Fastipy](https://pypi.org/project/Fastipy/) is a fast and easy-to-use open source Python library for developing RESTful APIs.\n\nPowered by **[uvicorn](https://www.uvicorn.org/)**\n\n## Installation\n\n```bash\npip install fastipy\n```\n\n## Examples\n\n### Example for GET Route with Query Params\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\n# Routes can be async or sync functions, but reply send functions are async\n# The handler returns the default HTTP status code 200\n@app.get(\"/\")\ndef home(req: Request, _):\n # Get query params age\n age = req.query[\"age\"]\n # Example: Recovery all persons from database with this age and print the html\n print(\"<h1>Retrieving all persons</h1><ul><li>A Person</li></ul>\")\n```\n\n### Example for GET Route with Params, CORS and multiple methods\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy().cors()\n\n@app.get(\"/user/:id\")\n@app.post(\"/user/:id\")\nasync def getUser(req: Request, reply: Reply):\n # get users from database\n for i in users:\n if i[\"id\"] == req.params[\"id\"]:\n # All response functions are asynchronous\n return await reply.send(i)\n\n await reply.send_code(404)\n```\n\n### Example for POST Route with Body\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\n@app.post(\"/user\")\nasync def createUser(req: Request, reply: Reply):\n user = req.body.json\n # Save user in database\n await reply.code(201).send(\"Created\")\n```\n\n### Example for PUT Route with Body\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\n@app.put(\"/user\")\nasync def createUser(req: Request, reply: Reply):\n user = req.body.json\n # Update user in database\n await reply.type(\"text/html\").code(201).send(\"<h1>Created</h1>\")\n```\n\n### Example for GET Route with file stream\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\n@app.get(\"/stream\")\nasync def streamFile(_, reply: Reply):\n # It could be an asynchronous generator\n def generator():\n with open(\"file.txt\") as f:\n for line in f:\n yield line\n\n await reply.send(generator())\n```\n\n### Adding custom serializer to Reply send\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\napp.add_serializer(\n validation=lambda data: isinstance(data, str),\n serializer=lambda data: (\"application/json\", json.dumps({\"error\": data})),\n)\n\n@app.get(\"/\")\nasync def customSerializer(_, reply: Reply):\n await reply.code(404).send(\"Field not found\")\n```\n\n### Running\n\nRunning Fastipy application in development is easy\n\n```py\nimport uvicorn\n\nif __name__ == \"__main__\":\n # main:app indicates the FILE:VARIABLE\n\n # The file is the main file where Fastipy() is instantiated\n # The variable is the name of the variable that contains the instance of Fastipy()\n\n # You can find more configurations here https://www.uvicorn.org/\n\n # set reload to True for automatic reloading!\n uvicorn.run(\"main:app\", log_level=\"debug\", port=8000, reload=True, loop=\"asyncio\")\n```\n\n### See more examples in **[examples](https://github.com/Bielgomes/Fastipy/tree/main/examples)** folder\n\n## Creating plugins\n\n```py\n# chat.py\nfrom fastipy import FastipyInstance, Reply\n\n# Plugins can be asynchronous or synchronized functions\n# Plugins have access to the main instance, which means they can use all of Fastipy's functions\ndef chatRoutes(app: FastipyInstance, options: dict):\n @app.get(\"/\")\n async def index(_, reply: Reply):\n await reply.send_code(200)\n\n @app.get(\"/chat\")\n async def test(_, reply: Reply):\n await reply.send_code(200)\n```\n\n```py\n# message.py\nfrom fastipy import FastipyInstance, Reply\n\nasync def messageRoutes(app: FastipyInstance, options: dict):\n @message.get(\"/\")\n async def index(_, reply: Reply):\n await reply.send_code(200)\n\n @message.get(\"/message\")\n async def test(_, reply: Reply):\n await reply.send_code(200)\n\n app.name(\"custom plugin name\")\n```\n\n```py\n# main.py\nfrom fastipy import Fastipy\n\nfrom message import messageRoutes\nfrom chat import chatRoutes\n\napp = Fastipy().cors()\n\napp.register(messageRoutes, {\"prefix\": \"/message\"})\napp.register(chatRoutes, {\"prefix\": \"/chat\"})\n```\n\n## Hooks\n\n```py\nfrom fastipy import Fastipy, Request, Reply\n\napp = Fastipy()\n\n# The preHandler hook is called before the request handler\n@app.hook(\"preHandler\")\ndef preHandler(req: Request, reply: Reply):\n print(\"onRequest hook\")\n\n# The onRequest hook is called when the request is handled\n@app.hook(\"onRequest\")\ndef onRequest(req: Request, reply: Reply):\n print(\"onRequest hook\")\n\n# The onResponse hook is called when the reply sends a response\n@app.hook(\"onResponse\")\ndef onResponse(req: Request, reply: Reply):\n print(\"onResponse hook\")\n\n# The onError hook is called when an error occurs\n@app.hook(\"onError\")\ndef onError(error: Exception, req: Request, reply: Reply):\n print(f\"onError hook exception: {error}\")\n\n# A hook will only be linked to a route if its declaration precedes the route\n# The order of hooks of the same type is important\n@app.get(\"/\")\nasync def index(_, reply: Reply):\n await reply.send_code(200)\n```\n\n## End to End tests\n\n```py\n# See more in https://www.starlette.io/testclient/\nfrom fastipy import TestClient\nfrom main import app\n\nclient = TestClient(app)\n\nresponse = client.post(\"/\")\nassert response.status_code == 200\nassert response.text == \"Hello World\"\n```\n\n# Application Deploy\n\nFor production deployment, please refer to this **[uvicorn guide](https://www.uvicorn.org/deployment/)**.\n\n# Change Log\n\n## Development Version 1.5.4\n\n### Todo\n\n### Added\n\n- [X] CI with Github Actions.\n- [X] Code Coverage with Codecov.\n\n### Changed\n\n- [X] Change the routes handler to use the Raw Path instead of the Path (This Change allow the use of %20 %2F and other special characters in the path).\n- [X] Improve functionality to handle asynchronous functions in synchronous environments (This change probably fix Memory Leak in the Fastipy).\n\n### Fixed\n\n- [X] Fix the bug in the search route where special characters are not being URL-escaped.\n\n# Contributors\n\n<a href=\"https://github.com/Bielgomes/Fastipy/graphs/contributors\">\n <img src=\"https://contrib.rocks/image?repo=Bielgomes/Fastipy\"/>\n</a>\n\n## How to Contributing\n\nOpen pull request\n",
"bugtrack_url": null,
"license": "GPLv3",
"summary": "Fastipy is a fast and easy-to-use open source Python library for developing RESTful APIs. Inspired by the FastAPI and Fastify syntax and powered by uvicorn ASGI web server.",
"version": "1.5.4",
"project_urls": {
"Bug tracker": "https://github.com/Bielgomes/fastipy/issues",
"Code": "https://github.com/Bielgomes/fastipy",
"Homepage": "https://github.com/Bielgomes/Fastipy",
"Repository": "https://github.com/Bielgomes/Fastipy"
},
"split_keywords": [
"restful",
" api",
" asgi",
" fastapi",
" fastipy",
" web server"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b146b52bdf8e96fb354ba2cbdb47ba672f9de515f50185b92805941b27424f0d",
"md5": "dfb2e8baaea9029bf7df1693e8bf119b",
"sha256": "e679b3ee54b2f87889c618991a22e1613623e170f7f7a2655cf6fdd277fa1a1b"
},
"downloads": -1,
"filename": "fastipy-1.5.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "dfb2e8baaea9029bf7df1693e8bf119b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0.0,>=3.10.11",
"size": 52225,
"upload_time": "2024-11-02T05:49:02",
"upload_time_iso_8601": "2024-11-02T05:49:02.957604Z",
"url": "https://files.pythonhosted.org/packages/b1/46/b52bdf8e96fb354ba2cbdb47ba672f9de515f50185b92805941b27424f0d/fastipy-1.5.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "aa45ba82180bff27c32ed5225eac7c1d7743cd8aecf4ade12396984b9d26396c",
"md5": "fa2b2b7ed716bb7ba307a8a1099f5b7f",
"sha256": "6bcda22b54f1548b97026eb7c98954a354e96531215206b7ead738af831433ec"
},
"downloads": -1,
"filename": "fastipy-1.5.4.tar.gz",
"has_sig": false,
"md5_digest": "fa2b2b7ed716bb7ba307a8a1099f5b7f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0.0,>=3.10.11",
"size": 40600,
"upload_time": "2024-11-02T05:49:05",
"upload_time_iso_8601": "2024-11-02T05:49:05.035849Z",
"url": "https://files.pythonhosted.org/packages/aa/45/ba82180bff27c32ed5225eac7c1d7743cd8aecf4ade12396984b9d26396c/fastipy-1.5.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-02 05:49:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Bielgomes",
"github_project": "Fastipy",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fastipy"
}