usrv


Nameusrv JSON
Version 0.4.2 PyPI version JSON
download
home_pagehttps://moustikitos.github.io/micro-server
SummaryPure python micro web framework
upload_time2025-01-23 16:02:43
maintainerTHOORENS Bruno
docs_urlNone
authorTHOORENS Bruno
requires_pythonNone
licenseCpyright 2020 - 2021 THOORENS Bruno
keywords micro framework http
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # `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"
}
        
Elapsed time: 0.48541s