docs: python update (#2932)

This commit is contained in:
Luke Kingland 2025-07-23 07:38:07 +09:00 committed by GitHub
parent 837988484f
commit a3dac70419
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 7710 additions and 7365 deletions

View File

@ -1,160 +1,379 @@
# Python Function Developer's Guide # Python Function Developer's Guide
When creating a Python function using the `func` CLI, the project directory Python Functions allow for the direct deployment of source code as a production
looks like a typical Python project. Both HTTP and Event functions have the same service to any Kubernetes cluster with Knative installed. The request handler
template structure. method signature follows the ASGI (Asynchronous Server Gateway Interface)
standard, allowing for integration with any supporting library.
## Project Structure
When you create a Python function using `func create -l python`, you get a standard Python project structure:
``` ```
func create -l python fn func create -l python myfunc
Project path: /home/developer/src/fn tree myfunc
Function name: fn myfunc/
Runtime: python ├── func.yaml # Function configuration
├── pyproject.toml # Python project metadata
tree ├── function/
fn │ ├── __init__.py
├── func.py │ └── func.py # Your function implementation
├── func.yaml └── tests/
├── requirements.txt └── test_func.py # Unit tests
└── test_func.py
``` ```
Aside from the `func.yaml` file, this looks like the beginning of just about The `func.yaml` file contains build and deployment configuration. For details,
any Python project. For now, we will ignore the `func.yaml` file, and just see the [func.yaml reference](../reference/func_yaml.md).
say that it is a configuration file that is used when building your project.
If you're really interested, check out the [reference doc](../reference/func_yaml.md).
To learn more about the CLI and the details for each supported command, see
the [CLI Commands document](../reference/func.md).
## Running the function locally ## Function Implementation
To run a function, you'll first need to build it. This step creates an OCI Python functions must implement a method `new()` which returns a new instance
container image that can be run locally on your computer, or on a Kubernetes of your Function class:
cluster.
```
func build
```
After the function has been built, it can be run locally.
```
func run
```
Functions can be invoked with a simple HTTP request.
You can test to see if the function is working by using your browser to visit
http://localhost:8080. You can also access liveness and readiness
endpoints at http://localhost:8080/health/liveness and
http://localhost:8080/health/readiness. These two endpoints are used
by Kubernetes to determine the health of your function. If everything
is good, both of these will return `OK`.
## Deploying the function to a cluster
To deploy your function to a Kubernetes cluster, use the `deploy` command.
```
func deploy
```
You can get the URL for your deployed function with the `info` command.
```
func info
```
## Testing a function locally
Python functions can be tested locally on your computer. In the project there is
a `test_func.py` file which contains a simple unit test. To run the test locally,
you'll need to install the required dependencies. You do this as you would
with any Python project.
```
pip install -r requirements.txt
```
Once you have done this, you can run the provided tests with `python3 test_func.py`.
The default test framework for Python functions is `unittest`. If you prefer another,
that's no problem. Just install a test framework more to your liking.
## Function reference
Boson Python functions have very few restrictions. You can add any required dependencies
in `requirements.txt`, and you may include additional local Python files. The only real
requirements are that your project contain a `func.py` file which contains a `main()` function.
In this section, we will look in a little more detail at how Boson functions are invoked,
and what APIs are available to you as a developer.
### Invocation parameters
When using the `func` CLI to create a function project, you may choose to generate a project
that responds to a `CloudEvent` or simple HTTP. `CloudEvents` in Knative are transported over
HTTP as a `POST` request, so in many ways, the two types of functions are very much the same.
They each will listen and respond to incoming HTTP events.
When an incoming request is received, your function will be invoked with a `Context`
object as the first parameter. This object is a Python class with two attributes. The
`request` attribute will always be present, and contains the Flask `request` object.
The second attribute, `cloud_event`, will be populated if the incoming request is a
`CloudEvent`. Developers may access any `CloudEvent` data from the context object.
For example:
```python ```python
def main(context: Context): def new():
""" """Factory function that returns a Function instance."""
The context parameter contains the Flask request object and any return Function()
CloudEvent received with the request.
""" class Function:
print(f"Method: {context.request.method}") """Your function implementation."""
print(f"Event data {context.cloud_event.data}) pass
# ... business logic here
``` ```
### Return Values ### Core Methods
Functions may return any value supported by Flask, as the invocation framework
proxies these values directly to the Flask server. See the Flask
[documentation](https://flask.palletsprojects.com/en/1.1.x/quickstart/#about-responses)
for more information.
#### Example Your function class can implement several optional methods:
```python
def main(context: Context):
body = { "message": "Howdy!" }
headers = { "content-type": "application/json" }
return body, 200, headers
```
Note that functions may set both headers and response codes as secondary #### `handle(self, scope, receive, send)`
and tertiary response values from function invocation. The main request handler following ASGI protocol. This async method processes all HTTP requests except health checks.
### CloudEvents
All event messages in Knative are sent as `CloudEvents` over HTTP. As noted
above, function developers may access an event through the `context` parameter
when the function is invoked. Additionally, developers may use an `@event`
decorator to inform the invoker that this function's return value should be
converted to a `CloudEvent` before sending the response. For example:
```python ```python
@event("event_source"="/my/function", "event_type"="my.type") async def handle(self, scope, receive, send):
def main(context): """Handle HTTP requests."""
# business logic here # Process the request
data = do_something() await send({
# more data processing 'type': 'http.response.start',
return data 'status': 200,
'headers': [[b'content-type', b'text/plain']],
})
await send({
'type': 'http.response.body',
'body': b'Hello, World!',
})
``` ```
This will result in a `CloudEvent` as the response value, with a type of #### `start(self, cfg)`
`"my.type"`, a source of `"/my/function"`, and the data property set to `data`.
Both the `event_source` and `event_type` decorator attributes are optional.
If not supplied, the CloudEvent's source attribute will be set to
`"/parliament/function"` and the type will be set to `"parliament.response"`.
## Dependencies Called when a function instance starts (e.g., during scaling or updates). Receives configuration as a dictionary.
Developers are not restricted to the dependencies provided in the template
`requirements.txt` file. Additional dependencies can be added as they would be ```python
in any other project by simply adding them to the `requirements.txt` file. def start(self, cfg):
When the project is built for deployment, these dependencies will be included """Initialize function with configuration."""
in the container image. self.debug = cfg.get('DEBUG', 'false').lower() == 'true'
logging.info("Function initialized")
```
#### `stop(self)`
Called when a function instance stops. Use for cleanup operations.
```python
def stop(self):
"""Clean up resources."""
# Close database connections, flush buffers, etc.
logging.info("Function shutting down")
```
#### `alive(self)` and `ready(self)`
Health check methods exposed at `/health/liveness` and `/health/readiness`:
```python
def alive(self):
"""Liveness check."""
return True, "Function is alive"
def ready(self):
"""Readiness check."""
if self.database_connected:
return True, "Ready to serve"
return False, "Database not connected"
```
## Local Development
### Running Your Function
```bash
# Build and run on the host (not in a container)
func run --builder=host
# Force rebuild even if no changes detected
func run --build
```
### Testing
Test your function with HTTP requests:
```bash
# Test the main endpoint
curl http://localhost:8080
# Check health endpoints
curl http://localhost:8080/health/liveness
curl http://localhost:8080/health/readiness
```
### Testing CloudEvent Functions
Create a CloudEvent function:
```bash
# Create a new CloudEvent function
func create -l python -t cloudevents myeventfunc
```
Test CloudEvent functions using curl with proper headers:
```bash
# Invoke with a CloudEvent
curl -X POST http://localhost:8080 \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: com.example.sampletype" \
-H "Ce-Source: example/source" \
-H "Ce-Id: 1234-5678-9101" \
-H "Ce-Subject: example-subject" \
-H "Content-Type: application/json" \
-d '{"message": "Hello CloudEvent!"}'
```
Also see `func invoke` which automates this for basic testing.
### Unit Testing
Python functions use modern Python packaging with `pyproject.toml` and include
pytest with async support for testing ASGI functions. The generated project
includes example tests in `tests/test_func.py` that demonstrate how to test the
async handler.
#### Setting Up Your Development Environment
It's best practice to use a virtual environment to isolate your function's dependencies:
```bash
# Create a virtual environment (Python 3.3+)
python3 -m venv venv
# Activate the virtual environment
# On Linux/macOS:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate
# Upgrade pip to ensure you have the latest version
python -m pip install --upgrade pip
# Install the function package and its dependencies (including test dependencies)
pip install -e .
# Run tests with pytest
pytest
# Run tests with verbose output
pytest -v
# Run tests with coverage (requires pytest-cov)
pip install pytest-cov
pytest --cov=function --cov-report=term-missing
# When done, deactivate the virtual environment
deactivate
```
**Note**:
- Python 3 typically comes with `venv` module built-in
- If `python3` command is not found, try `python` instead
- The `-m pip` syntax ensures you're using the pip from your virtual environment
- Always activate your virtual environment before running tests or installing dependencies
#### Writing Tests for ASGI Functions
The test file demonstrates how to test ASGI functions by mocking the ASGI interface:
```python
import pytest
from function import new
@pytest.mark.asyncio
async def test_function_handle():
# Create function instance
f = new()
# Mock ASGI scope (request details)
scope = {
'type': 'http',
'method': 'POST',
'path': '/',
'headers': [(b'content-type', b'application/json')],
}
# Mock receive callable (for request body)
async def receive():
return {
'type': 'http.request',
'body': b'{"test": "data"}',
'more_body': False,
}
# Track sent responses
responses = []
# Mock send callable
async def send(message):
responses.append(message)
# Call the handler
await f.handle(scope, receive, send)
# Assert responses
assert len(responses) == 2
assert responses[0]['type'] == 'http.response.start'
assert responses[0]['status'] == 200
assert responses[1]['type'] == 'http.response.body'
```
#### Testing CloudEvent Functions
For CloudEvent functions, include CloudEvent headers in your test scope:
```python
@pytest.mark.asyncio
async def test_cloudevent_handler():
f = new()
# CloudEvent headers
scope = {
'type': 'http',
'method': 'POST',
'path': '/',
'headers': [
(b'ce-specversion', b'1.0'),
(b'ce-type', b'com.example.test'),
(b'ce-source', b'test/unit'),
(b'ce-id', b'test-123'),
(b'content-type', b'application/json'),
],
}
# Test with CloudEvent data
async def receive():
return {
'type': 'http.request',
'body': b'{"message": "test event"}',
'more_body': False,
}
# ... rest of test
```
#### Testing with Real HTTP Clients
For integration testing, you can use httpx with ASGI support:
```python
import httpx
import pytest
from function import new
@pytest.mark.asyncio
async def test_with_http_client():
f = new()
# Create ASGI transport with your function
transport = httpx.ASGITransport(app=f.handle)
# Make HTTP requests
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/")
assert response.status_code == 200
response = await client.post("/", json={"test": "data"})
assert response.status_code == 200
```
## Advanced Implementation Examples
### Processing Request Data
```python
async def handle(self, scope, receive, send):
"""Process POST requests with JSON data."""
if scope['method'] == 'POST':
# Receive request body
body = b''
while True:
message = await receive()
if message['type'] == 'http.request':
body += message.get('body', b'')
if not message.get('more_body', False):
break
# Process JSON data
import json
data = json.loads(body)
result = process_data(data)
# Send response
response_body = json.dumps(result).encode()
await send({
'type': 'http.response.start',
'status': 200,
'headers': [[b'content-type', b'application/json']],
})
await send({
'type': 'http.response.body',
'body': response_body,
})
```
### Environment-Based Configuration
```python
class Function:
def start(self, cfg):
"""Configure function from environment."""
self.api_key = cfg.get('API_KEY')
self.cache_ttl = int(cfg.get('CACHE_TTL', '300'))
self.log_level = cfg.get('LOG_LEVEL', 'INFO')
logging.basicConfig(level=self.log_level)
```
### CloudEvent Handling
For CloudEvent support, parse the headers and body accordingly:
```python
async def handle(self, scope, receive, send):
"""Handle CloudEvents."""
headers = dict(scope['headers'])
# Check if this is a CloudEvent
if b'ce-type' in headers:
event_type = headers[b'ce-type'].decode()
event_source = headers[b'ce-source'].decode()
# Process CloudEvent...
```
## Deployment
### Basic Deployment
```bash
# Deploy to a specific registry
func deploy --builder=host --registry docker.io/myuser
```
For all deploy options, see `func deploy --help`

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,50 @@
# Python CloudEvents Function # Python CloudEvents Function
Welcome to your new Python Function! A minimal Function implementation can Welcome to your new Python Function! A minimal Function implementation can
be found in ./function/func.py. be found in `./function/func.py`.
For more, run `func --help` or read the [Python Functions doc](https://github.com/knative/func/blob/main/docs/function-templates/python.md)
## Usage
Knative Functions allow for the deployment of your source code directly to a
Kubernetes cluster with Knative installed.
### Function Structure
Python functions must implement a `new()` method that returns a function
instance. The function class can optionally implement:
- `handle()` - Process CloudEvent requests
- `start()` - Initialize the function with configuration
- `stop()` - Clean up resources when the function stops
- `alive()` / `ready()` - Health and readiness checks
See the default implementation in `./function/func.py`
### Running Locally
Use `func run` to test your Function locally before deployment.
For development environments where Python is installed, it's suggested to use
the `host` builder as follows:
```bash
# Run function on the host (outsdie of a container)
func run --builder=host
```
### Deploying
Use `func deploy` to deploy your Function to a Knative-enabled cluster:
```bash
# Deploy with interactive prompts (recommended for first deployment)
func deploy --registry ghcr.io/myuser
```
Functions are automatically built, containerized, and pushed to a registry
before deployment. Subsequent deployments will update the existing function.
## Roadmap
Our project roadmap can be found: https://github.com/orgs/knative/projects/49
For more, see [the complete documentation]('https://github.com/knative/func/tree/main/docs')

View File

@ -1,6 +1,50 @@
# Python HTTP Function # Python HTTP Function
Welcome to your new Python Function! A minimal Function implementation can Welcome to your new Python Function! A minimal Function implementation can
be found in func.py. be found in `./function/func.py`.
For more, run `func --help` or read the [Python Functions doc](https://github.com/knative/func/blob/main/docs/function-templates/python.md)
## Usage
Knative Functions allow for the deployment of your source code directly to a
Kubernetes cluster with Knative installed.
### Function Structure
Python functions must implement a `new()` method that returns a function
instance. The function class can optionally implement:
- `handle()` - Process HTTP requests
- `start()` - Initialize the function with configuration
- `stop()` - Clean up resources when the function stops
- `alive()` / `ready()` - Health and readiness checks
See the default implementation in `./function/func.py`
### Running Locally
Use `func run` to test your Function locally before deployment.
For development environments where Python is installed, it's suggested to use
the `host` builder as follows:
```bash
# Run function on the host (outside of a container)
func run --builder=host
```
### Deploying
Use `func deploy` to deploy your Function to a Knative-enabled cluster:
```bash
# Deploy with interactive prompts (recommended for first deployment)
func deploy --registry ghcr.io/myuser
```
Functions are automatically built, containerized, and pushed to a registry
before deployment. Subsequent deployments will update the existing function.
## Roadmap
Our project roadmap can be found: https://github.com/orgs/knative/projects/49
For more, see [the complete documentation]('https://github.com/knative/func/tree/main/docs')