Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 97 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,137 @@ OpenFaaS Python Flask Templates
The Python Flask templates that make use of the incubator project [of-watchdog](https://github.com/openfaas-incubator/of-watchdog).

Templates available in this repository:

- python27-flask
- python3-flask
- python3-flask-debian
- python3-flask-armhf

- python3-http
- python3-http-debian
- python3-http-armhf

Notes:
- To build and deploy a function for Raspberry Pi or ARMv7 in general, use the language templates ending in *-armhf*

## Picking your template

The templates named `python*-flask*` are designed as a drop-in replacement for the classic `python3` template, but using the more efficient of-watchdog. The move to use flask as an underlying framework allows for greater control over the HTTP request and response.

Those templates named `python*-http*` are designed to offer full control over the HTTP request and response. Flask is used as an underlying framework.

The `witness` HTTP server is used along with Flask for all templates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there is a typo, it is waitress not witness


Are you referencing pip modules which require a native build toolchain? It's advisable to use the template with a `-debian` suffix in this case. The Debian images are larger, however they are usually more efficient for use with modules like `numpy` and `pandas`.

## Downloading the templates

Using template pull:

```bash
faas template pull https://github.com/openfaas-incubator/python-flask-template
```
$ faas template pull https://github.com/openfaas-incubator/python-flask-template

Using template store:

```bash
faas template store pull python3-flask
```

# Using the python27-flask/python3-flask templates
# Using the python3-flask template

Create a new function

```
$ faas new --lang python27-flask <fn-name>
export OPENFAAS_PREFIX=alexellis2
export FN="tester"
faas new --lang python3-flask $FN
```

Build, push, and deploy

```
$ faas up -f <fn-name>.yml
faas up -f $FN.yml
```

Test the new function

```
$ echo -n content | faas invoke <fn-name>
echo -n content | faas invoke $FN
```

## Example of returning a string

```python
def handle(req):
"""handle a request to the function
Args:
req (str): request body
"""

return "Hi" + str(req)
```

## Example of returning a custom HTTP code

```python
def handle(req):
return "request accepted", 201
```

## Example of returning a custom HTTP code and content-type

```python
def handle(req):
return "request accepted", 201, {"Content-Type":"binary/octet-stream"}
```

## Example of accepting raw bytes in the request

Update stack.yml:

```yaml
environment:
RAW_BODY: True
```

> Note: the value for `RAW_BODY` is case-sensitive.

```python
def handle(req):
"""handle a request to the function
Args:
req (str): request body
"""

# req is bytes, so an input of "hello" returns i.e. b'hello'
return string(req)
```

# Using the python3-http templates

Create a new function

```
$ faas new --lang python3-http <fn-name>
export OPENFAAS_PREFIX=alexellis2
export FN="tester"
faas new --lang python3-http $FN
```

Build, push, and deploy

```
$ faas up -f <fn-name>.yml
```
Set your OpenFaaS gateway URL. For example:
```
$ OPENFAAS_URL=http://127.0.0.1:8080
faas up -f $FN.yml
```

Test the new function

```
$ curl -i $OPENFAAS_URL/function/<fn-name>
echo -n content | faas invoke $FN
```

## Event and Context Data

The function handler is passed two arguments, *event* and *context*.

*event* contains data about the request, including:
Expand All @@ -66,12 +148,15 @@ The function handler is passed two arguments, *event* and *context*.
- hostname

## Response Bodies

By default, the template will automatically attempt to set the correct Content-Type header for you based on the type of response.

For example, returning a dict object type will automatically attach the header `Content-Type: application/json` and returning a string type will automatically attach the `Content-Type: text/html, charset=utf-8` for you.

## Example usage

### Custom status codes and response bodies

Successful response status code and JSON response body
```python
def handle(event, context):
Expand Down
14 changes: 13 additions & 1 deletion template/python3-flask-armhf/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
from flask import Flask, request
from function import handler
from waitress import serve
import os

app = Flask(__name__)

# distutils.util.strtobool() can throw an exception
def is_true(val):
return len(val) > 0 and val.lower() == "true" or val == "1"

@app.before_request
def fix_transfer_encoding():
"""
Expand All @@ -22,7 +27,14 @@ def fix_transfer_encoding():
@app.route("/", defaults={"path": ""}, methods=["POST", "GET"])
@app.route("/<path:path>", methods=["POST", "GET"])
def main_route(path):
ret = handler.handle(request.get_data())
raw_body = os.getenv("RAW_BODY")

as_text = True

if is_true(raw_body):
as_text = False

ret = handler.handle(request.get_data(as_text=as_text))
return ret

if __name__ == '__main__':
Expand Down
14 changes: 13 additions & 1 deletion template/python3-flask-debian/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
from flask import Flask, request
from function import handler
from waitress import serve
import os

app = Flask(__name__)

# distutils.util.strtobool() can throw an exception
def is_true(val):
return len(val) > 0 and val.lower() == "true" or val == "1"

@app.before_request
def fix_transfer_encoding():
"""
Expand All @@ -22,7 +27,14 @@ def fix_transfer_encoding():
@app.route("/", defaults={"path": ""}, methods=["POST", "GET"])
@app.route("/<path:path>", methods=["POST", "GET"])
def main_route(path):
ret = handler.handle(request.get_data())
raw_body = os.getenv("RAW_BODY")

as_text = True

if is_true(raw_body):
as_text = False

ret = handler.handle(request.get_data(as_text=as_text))
return ret

if __name__ == '__main__':
Expand Down
14 changes: 13 additions & 1 deletion template/python3-flask/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
from flask import Flask, request
from function import handler
from waitress import serve
import os

app = Flask(__name__)

# distutils.util.strtobool() can throw an exception
def is_true(val):
return len(val) > 0 and val.lower() == "true" or val == "1"

@app.before_request
def fix_transfer_encoding():
"""
Expand All @@ -22,7 +27,14 @@ def fix_transfer_encoding():
@app.route("/", defaults={"path": ""}, methods=["POST", "GET"])
@app.route("/<path:path>", methods=["POST", "GET"])
def main_route(path):
ret = handler.handle(request.get_data())
raw_body = os.getenv("RAW_BODY")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


as_text = True
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To enable easier migration from the python3 template


if is_true(raw_body):
as_text = False

ret = handler.handle(request.get_data(as_text=as_text))
return ret

if __name__ == '__main__':
Expand Down