# `usrv`: the lightest python web framework
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/Moustikitos/micro-server/master/LICENSE)
Package (`usrv`) is a pure python micro server implementation. It allows python function bindings for all HTTP methods. HTTP body can be encrypted on demand using secp256k1 keypairs.
## Install
### Developpment version
```bash
$ python -m pip install git+https://github.com/Moustikitos/micro-server#egg=usrv
```
### last version (0.4.2)
```bash
$ python -m pip install usrv
```
## `usrv.route`
Bind python code to any HTTP requests easily using decorator syntax.
`route` module can be used in standalone mode outside of `usrv` package.
## `usrv.app`
Run a low footprint python server or [PEP#3333 WSGI server](https://www.python.org/dev/peps/pep-3333).
```python
import waitress # wsgi server for windows
from usrv import app, route
@route.bind("/index")
def index(**kw):
return 200, "Index page", kw
waitress.serve(app.uApp(), threads=2)
```
## Fast and simple
Let's create a server with `/test` endpoint in a python module named `test.py`:
```python
from usrv import route, app
@route.bind("/test")
def do_test(a, b):
# write some code and return something
return 200, a, b
def launchApp():
route.run(host="127.0.0.1", port=5000, loglevel=20)
```
**Bound functions have to return a tuple with a valid HTTP status code as first item**.
Server can be run from python interpreter:
```python
>>> import test
>>> test.launchApp()
INFO:usrv.srv:listening on 127.0.0.1:5000
CTRL+C to stop...
```
Now going to `http://127.0.0.1:5000/test` with any browser gives:
```json
[null, null]
```
## Extracting values from url query
`[null, null]` above are the returned values `a` and `b` from `do_test` function. Values can be extracted from query string. Let's type `http://127.0.0.1:5000/test?b=12&a=Paris` in the address bar:
```json
["Paris", "12"]
```
Returned value from query are `str` only. Unexpected values are ignored but there is a [convenient way to catch them](#catching-unexpected-values).
## Extracting values from url path
Values can also be extracted from url path with or without a typing precision.
```python
@route.bind("/<int:b>/<a>")
def do_test(a, b):
# write some code and return something
return 200, a, b
```
This binding creates multiple endpoint possibilities. Let's try `http://127.0.0.1:5000/5/test`:
```json
["test", 5]
```
Values from url can be overrided by thoses from query... `http://127.0.0.1:5000/5/test?a=2&b=6`:
```json
["2", "6"]
```
> It can only be overrided with `str` type values.
## Catching unexpected values
Using varargs or/and keywordargs is a convenient way to catch unexpected values from url query and HTTP context. HTTP Context is defined an headers and data (HTTP requests with body).
When HTTP context is catched by `*args`, unexpected values from query string are appended next.
Url used for this chapter `http://127.0.0.1:5000/test?b=12&a=Paris&unexpected=there`.
### Variable args (`*args`)
```python
@route.bind("/test")
def do_test(a, b, *args):
# write some code and return something
# args is a tuple
return 200, a, b, args
```
> With `*args` method, HTTP headers and data will be postionned at the end of `json` response
```json
[
"Paris",
"12",
"there",
"GET",
{
"host": "127.0.0.1:5000",
"connection": "keep-alive",
"sec-ch-ua": "\"Brave\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"sec-gpc": "1",
"accept-language": "fr-FR,fr",
"sec-fetch-site": "none",
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"sec-fetch-dest": "document",
"accept-encoding": "gzip, deflate, br, zstd"
},
null
]
```
### Keyword args (`**kwargs`)
```python
@route.bind("/test")
def do_test(a, b, **kwargs):
# write some code and return something
# kwargs is a dict
return 200, a, b, kwargs
```
> using `**kwargs` is the recommended way to retrieve unexpected values by names. Unexpected mapping is positionned at the end of `json` response.
```json
[
"Paris",
"12",
{
"unexpected": "there",
"method": "GET",
"headers": {
"host": "127.0.0.1:5000",
"connection": "keep-alive",
"sec-ch-ua": "\"Brave\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"sec-gpc": "1",
"accept-language": "fr-FR,fr",
"sec-fetch-site": "none",
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"sec-fetch-dest": "document",
"accept-encoding": "gzip, deflate, br, zstd"
},
"data": null,
}
]
```
## Command line
WSGI server can be launched from command line.
```bash
$ python wsgi_srv.py -h
Usage: wsgi_srv.py [options] BINDINGS...
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-t THREADS, --threads=THREADS
set thread number [default: 2]
-l LOGLEVEL, --log-level=LOGLEVEL
set log level from 1 to 100 [default: 20]
-i HOST, --ip=HOST ip to run from [default: 127.0.0.1]
-p PORT, --port=PORT port to use [default: 5000]
```
`BINDINGS` is a space-separated-list of python module names (ie no `*.py` extention) containing boud python functions. Modules containing bound functions have to be in one of `sys.path` folder. Specific folder can be added using `wsgi_srv.path` file.
## Changes
### 0.4.1
- [x] major changes and improvement, no backward compatibility with 0.3.0
### 0.4.2
- [x] improved `route.uHTTPRequstHandler` response
- [x] added body encryption/decryption based on `secp256k1` and `AES`
- [x] added multipart/form-data body management to req module
- [x] added custom client/server identification
- [x] added secured secret dump and load
- [x] added replay attack mitigation
- [x] added endpoint builder
- [x] added [pinata](https://pinata.cloud) plugin
- [x] added [binance](https://binance.com) plugin
### 0.4.3 (dev)
## Support this project
[![Liberapay receiving](https://img.shields.io/liberapay/goal/Toons?logo=liberapay)](https://liberapay.com/Toons/donate)
[![Paypal me](https://img.shields.io/badge/PayPal-toons-00457C?logo=paypal&logoColor=white)](https://paypal.me/toons)
<!-- [![Bitcoin](https://img.shields.io/badge/Donate-bc1q6aqr0hfq6shwlaux8a7ydvncw53lk2zynp277x-ff9900?logo=bitcoin)](https://raw.githubusercontent.com/Moustikitos/python-mainsail/master/docs/img/bc1q6aqr0hfq6shwlaux8a7ydvncw53lk2zynp277x.png) -->
Raw data
{
"_id": null,
"home_page": "https://moustikitos.github.io/micro-server",
"name": "usrv",
"maintainer": "THOORENS Bruno",
"docs_url": null,
"requires_python": null,
"maintainer_email": "moustikitos@gmail.com",
"keywords": "micro, framework, HTTP",
"author": "THOORENS Bruno",
"author_email": "moustikitos@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/06/e0/fd48fff3ece383f36ed48b93c232616a26d02da9c64deea63f40499adeb7/usrv-0.4.2.tar.gz",
"platform": null,
"description": "# `usrv`: the lightest python web framework\r\n\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/Moustikitos/micro-server/master/LICENSE)\r\n\r\nPackage (`usrv`) is a pure python micro server implementation. It allows python function bindings for all HTTP methods. HTTP body can be encrypted on demand using secp256k1 keypairs.\r\n\r\n## Install\r\n\r\n### Developpment version\r\n\r\n```bash\r\n$ python -m pip install git+https://github.com/Moustikitos/micro-server#egg=usrv\r\n```\r\n\r\n### last version (0.4.2)\r\n\r\n```bash\r\n$ python -m pip install usrv\r\n```\r\n\r\n## `usrv.route`\r\n\r\nBind python code to any HTTP requests easily using decorator syntax.\r\n`route` module can be used in standalone mode outside of `usrv` package.\r\n\r\n## `usrv.app`\r\n\r\nRun a low footprint python server or [PEP#3333 WSGI server](https://www.python.org/dev/peps/pep-3333).\r\n\r\n```python\r\nimport waitress # wsgi server for windows\r\nfrom usrv import app, route\r\n\r\n@route.bind(\"/index\")\r\ndef index(**kw):\r\n return 200, \"Index page\", kw\r\n\r\nwaitress.serve(app.uApp(), threads=2)\r\n```\r\n\r\n## Fast and simple\r\n\r\nLet's create a server with `/test` endpoint in a python module named `test.py`:\r\n\r\n```python\r\nfrom usrv import route, app\r\n\r\n@route.bind(\"/test\")\r\ndef do_test(a, b):\r\n # write some code and return something\r\n return 200, a, b\r\n\r\ndef launchApp():\r\n route.run(host=\"127.0.0.1\", port=5000, loglevel=20)\r\n```\r\n\r\n**Bound functions have to return a tuple with a valid HTTP status code as first item**.\r\nServer can be run from python interpreter:\r\n\r\n```python\r\n>>> import test\r\n>>> test.launchApp()\r\nINFO:usrv.srv:listening on 127.0.0.1:5000\r\nCTRL+C to stop...\r\n```\r\n\r\nNow going to `http://127.0.0.1:5000/test` with any browser gives:\r\n\r\n```json\r\n[null, null]\r\n```\r\n\r\n## Extracting values from url query\r\n\r\n`[null, null]` above are the returned values `a` and `b` from `do_test` function. Values can be extracted from query string. Let's type `http://127.0.0.1:5000/test?b=12&a=Paris` in the address bar:\r\n\r\n```json\r\n[\"Paris\", \"12\"]\r\n```\r\n\r\nReturned value from query are `str` only. Unexpected values are ignored but there is a [convenient way to catch them](#catching-unexpected-values).\r\n\r\n## Extracting values from url path\r\n\r\nValues can also be extracted from url path with or without a typing precision.\r\n\r\n```python\r\n@route.bind(\"/<int:b>/<a>\")\r\ndef do_test(a, b):\r\n # write some code and return something\r\n return 200, a, b\r\n```\r\n\r\nThis binding creates multiple endpoint possibilities. Let's try `http://127.0.0.1:5000/5/test`:\r\n\r\n```json\r\n[\"test\", 5]\r\n```\r\n\r\nValues from url can be overrided by thoses from query... `http://127.0.0.1:5000/5/test?a=2&b=6`:\r\n\r\n```json\r\n[\"2\", \"6\"]\r\n```\r\n\r\n> It can only be overrided with `str` type values.\r\n\r\n## Catching unexpected values\r\n\r\nUsing varargs or/and keywordargs is a convenient way to catch unexpected values from url query and HTTP context. HTTP Context is defined an headers and data (HTTP requests with body).\r\n\r\nWhen HTTP context is catched by `*args`, unexpected values from query string are appended next.\r\n\r\nUrl used for this chapter `http://127.0.0.1:5000/test?b=12&a=Paris&unexpected=there`.\r\n\r\n### Variable args (`*args`)\r\n\r\n```python\r\n@route.bind(\"/test\")\r\ndef do_test(a, b, *args):\r\n # write some code and return something\r\n # args is a tuple\r\n return 200, a, b, args\r\n```\r\n\r\n> With `*args` method, HTTP headers and data will be postionned at the end of `json` response\r\n\r\n```json\r\n[\r\n \"Paris\",\r\n \"12\",\r\n \"there\",\r\n \"GET\",\r\n {\r\n \"host\": \"127.0.0.1:5000\",\r\n \"connection\": \"keep-alive\",\r\n \"sec-ch-ua\": \"\\\"Brave\\\";v=\\\"131\\\", \\\"Chromium\\\";v=\\\"131\\\", \\\"Not_A Brand\\\";v=\\\"24\\\"\",\r\n \"sec-ch-ua-mobile\": \"?0\",\r\n \"sec-ch-ua-platform\": \"\\\"Windows\\\"\",\r\n \"upgrade-insecure-requests\": \"1\",\r\n \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\r\n \"accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\r\n \"sec-gpc\": \"1\",\r\n \"accept-language\": \"fr-FR,fr\",\r\n \"sec-fetch-site\": \"none\",\r\n \"sec-fetch-mode\": \"navigate\",\r\n \"sec-fetch-user\": \"?1\",\r\n \"sec-fetch-dest\": \"document\",\r\n \"accept-encoding\": \"gzip, deflate, br, zstd\"\r\n },\r\n null\r\n]\r\n```\r\n\r\n### Keyword args (`**kwargs`)\r\n\r\n```python\r\n@route.bind(\"/test\")\r\ndef do_test(a, b, **kwargs):\r\n # write some code and return something\r\n # kwargs is a dict\r\n return 200, a, b, kwargs\r\n```\r\n\r\n> using `**kwargs` is the recommended way to retrieve unexpected values by names. Unexpected mapping is positionned at the end of `json` response.\r\n\r\n```json\r\n[\r\n \"Paris\",\r\n \"12\",\r\n {\r\n \"unexpected\": \"there\",\r\n \"method\": \"GET\",\r\n \"headers\": {\r\n \"host\": \"127.0.0.1:5000\",\r\n \"connection\": \"keep-alive\",\r\n \"sec-ch-ua\": \"\\\"Brave\\\";v=\\\"131\\\", \\\"Chromium\\\";v=\\\"131\\\", \\\"Not_A Brand\\\";v=\\\"24\\\"\",\r\n \"sec-ch-ua-mobile\": \"?0\",\r\n \"sec-ch-ua-platform\": \"\\\"Windows\\\"\",\r\n \"upgrade-insecure-requests\": \"1\",\r\n \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\r\n \"accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\r\n \"sec-gpc\": \"1\",\r\n \"accept-language\": \"fr-FR,fr\",\r\n \"sec-fetch-site\": \"none\",\r\n \"sec-fetch-mode\": \"navigate\",\r\n \"sec-fetch-user\": \"?1\",\r\n \"sec-fetch-dest\": \"document\",\r\n \"accept-encoding\": \"gzip, deflate, br, zstd\"\r\n },\r\n \"data\": null,\r\n }\r\n]\r\n```\r\n\r\n## Command line\r\n\r\nWSGI server can be launched from command line.\r\n\r\n```bash\r\n$ python wsgi_srv.py -h\r\nUsage: wsgi_srv.py [options] BINDINGS...\r\n\r\nOptions:\r\n --version show program's version number and exit\r\n -h, --help show this help message and exit\r\n -t THREADS, --threads=THREADS\r\n set thread number [default: 2]\r\n -l LOGLEVEL, --log-level=LOGLEVEL\r\n set log level from 1 to 100 [default: 20]\r\n -i HOST, --ip=HOST ip to run from [default: 127.0.0.1]\r\n -p PORT, --port=PORT port to use [default: 5000]\r\n```\r\n\r\n`BINDINGS` is a space-separated-list of python module names (ie no `*.py` extention) containing boud python functions. Modules containing bound functions have to be in one of `sys.path` folder. Specific folder can be added using `wsgi_srv.path` file.\r\n\r\n## Changes\r\n\r\n### 0.4.1\r\n\r\n- [x] major changes and improvement, no backward compatibility with 0.3.0\r\n\r\n### 0.4.2\r\n\r\n- [x] improved `route.uHTTPRequstHandler` response\r\n- [x] added body encryption/decryption based on `secp256k1` and `AES`\r\n- [x] added multipart/form-data body management to req module\r\n- [x] added custom client/server identification\r\n- [x] added secured secret dump and load\r\n- [x] added replay attack mitigation\r\n- [x] added endpoint builder\r\n- [x] added [pinata](https://pinata.cloud) plugin\r\n- [x] added [binance](https://binance.com) plugin\r\n\r\n### 0.4.3 (dev)\r\n\r\n## Support this project\r\n\r\n[![Liberapay receiving](https://img.shields.io/liberapay/goal/Toons?logo=liberapay)](https://liberapay.com/Toons/donate)\r\n[![Paypal me](https://img.shields.io/badge/PayPal-toons-00457C?logo=paypal&logoColor=white)](https://paypal.me/toons)\r\n<!-- [![Bitcoin](https://img.shields.io/badge/Donate-bc1q6aqr0hfq6shwlaux8a7ydvncw53lk2zynp277x-ff9900?logo=bitcoin)](https://raw.githubusercontent.com/Moustikitos/python-mainsail/master/docs/img/bc1q6aqr0hfq6shwlaux8a7ydvncw53lk2zynp277x.png) -->\r\n",
"bugtrack_url": null,
"license": "Cpyright 2020 - 2021 THOORENS Bruno",
"summary": "Pure python micro web framework",
"version": "0.4.2",
"project_urls": {
"Bug Reports": "https://github.com/Moustikitos/micro-server/issues",
"Download": "https://github.com/Moustikitos/micro-server/archive/master.zip",
"Funding": "https://github.com/Moustikitos/micro-server/?tab=readme-ov-file#support-this-project",
"Homepage": "https://moustikitos.github.io/micro-server",
"Source": "https://github.com/Moustikitos/micro-server"
},
"split_keywords": [
"micro",
" framework",
" http"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "06e0fd48fff3ece383f36ed48b93c232616a26d02da9c64deea63f40499adeb7",
"md5": "081d043cacb25978bfe03e912bf796b7",
"sha256": "814ffb7d385cddae27a950d153683d6fa4eb17cb74a0a238116125ce745805b3"
},
"downloads": -1,
"filename": "usrv-0.4.2.tar.gz",
"has_sig": false,
"md5_digest": "081d043cacb25978bfe03e912bf796b7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 31526,
"upload_time": "2025-01-23T16:02:43",
"upload_time_iso_8601": "2025-01-23T16:02:43.941677Z",
"url": "https://files.pythonhosted.org/packages/06/e0/fd48fff3ece383f36ed48b93c232616a26d02da9c64deea63f40499adeb7/usrv-0.4.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-23 16:02:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Moustikitos",
"github_project": "micro-server",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "usrv"
}