diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b98f651..e0e1f86 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,90 +15,55 @@ The build process requires several tools to be installed: 2. **uv** - Python package manager 3. **Rust toolchain** (stable) -4. **wasm32-unknown-unknown target** (required by build.rs) +4. **wasm32-unknown-unknown Rust target** (required by build.rs) ```bash rustup target add wasm32-unknown-unknown ``` -5. **wasm-tools** (required by build.rs for WIT merging and componentization) -6. **Viceroy** - Fastly's local testing server +5. **[wasm-tools](https://github.com/bytecodealliance/wasm-tools)** (required by build.rs for WIT merging and componentization) +6. **[Viceroy](https://github.com/fastly/Viceroy/releases)** - Fastly's local testing server -## Getting Started - -1. **Clone the repository** - ```bash - git clone - cd compute-sdk-python/build-tool-impl - ``` - -2. **Initialize submodules** (if applicable) - ```bash - git submodule update --init --recursive - ``` - -3. **Install Python dependencies** - ```bash - uv sync --extra dev --extra test - ``` +## Development Workflow -4. **Verify setup** - ```bash - make help # Should show available commands - ``` +The `fastly-compute-py` build tool is written in Rust. By default, the Makefile uses `cargo run` (DEV_MODE=1), which means: +- **No installation needed** for testing or use against examples; the tool runs directly via cargo +- **Always up-to-date.** Changes to Rust code are automatically picked up. +- **Fast incremental builds.** Cargo handles recompilation efficiently. -## Development Workflow +To work on the build tool, edit the Rust code in `crates/fastly-compute-py/`, then run `make` to build it. -### Building Examples +### Making Changes to the Build Tool -The default development workflow uses `cargo run` which automatically picks up Rust changes: +The build tool is written in Rust and lives in `crates/fastly-compute-py/`. After you make changes, the Makefile automatically rebuilds via `cargo run` when you build an example service, like this: ```bash -# Build an example make build/bottle-app.composed.wasm - -# Build all examples -make - -# Serve an example for testing -make serve EXAMPLE=bottle-app ``` -### Making Changes to the Build Tool - -The build tool is in `crates/fastly-compute-py/`. When you make changes: +You can also test the installed entry point, which lets Python code call the build tool: ```bash -# The build system automatically rebuilds via `cargo run` -make build/bottle-app.composed.wasm - -# Or test the installed entry point make DEV_MODE=0 build/bottle-app.composed.wasm ``` ### Code Quality -```bash -# Format code (Python + Rust) -make format - -# Check formatting -make format-check - -# Run linters (Python + Rust) -make lint +These will spruce up spelling in both Python and Rust code: -# Auto-fix linting issues -make lint-fix +```bash +make format # Format code +make format-check # Check formatting +make lint # Run linters +make lint-fix # Auto-fix linting issues ``` ### Testing -```bash -# Run all tests -make test +The SDK has comprehensive tests, with integration tests running via Viceroy: -# Update snapshot tests -make test-update-snapshots +```bash +make test # Run all tests +make test-update-snapshots # Update snapshot tests ``` ## Project Structure @@ -109,7 +74,7 @@ make test-update-snapshots │ ├── fastly-compute-py/ # Rust build tool │ │ ├── build.rs # Build script (requires wasm-tools) │ │ └── src/ -│ └── wasiless/ # WASM component for WASI removal +│ └── wasiless/ # Wasm component for WASI stubbing ├── examples/ # Example applications │ ├── bottle-app/ │ ├── flask-app/ @@ -123,15 +88,15 @@ make test-update-snapshots Understanding the build process helps when debugging issues: -1. **build.rs runs** (during Rust compilation): +1. **build.rs** (during Rust compilation)... - Calls `wasm-tools component wit` to merge WIT files - Builds `wasiless` crate for wasm32-unknown-unknown - Calls `wasm-tools component new` to componentize wasiless -2. **fastly-compute-py runs**: +2. **fastly-compute-py**... - Resolves Python dependencies from virtualenv - - Calls `componentize-py` to build Python WASM component - - Composes with wasiless using WAC + - Calls `componentize-py` to build Python Wasm component + - Composes with wasiless using `wac` ## Continuous Integration diff --git a/README.md b/README.md index 3d27967..ff75c2d 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,57 @@ # Fastly Compute Python SDK -Experimental Python SDK for [Fastly Compute](https://www.fastly.com/products/edge-compute) services. +Experimental Python SDK for [Fastly Compute](https://www.fastly.com/products/edge-compute) services -## Features +## Highlights -- **Multiple Framework Support**: Examples with Bottle, Flask, and more -- **WIT Bindings**: Auto-generated Python bindings from Fastly's WIT files -- **WSGI Compatibility**: Host any WSGI-compatible web framework -- **Testing Framework**: Comprehensive viceroy-based integration testing -- **Type Safety**: Full type hints and IDE support +- **Support for WSGI Frameworks**. Flask and Bottle examples are included for reference. +- Access from Python to **Fastly's API** +- Full **type hints** and IDE support ## Quick Start -### Build and Run -```bash -make serve # Serve default example (Bottle) -make serve EXAMPLE=flask-app # Serve Flask example -``` +Here's how to write your own Python WSGI app and run it on Fastly's edge network: -Visit http://127.0.0.1:7676/hello/world or http://127.0.0.1:7676/info +1. Install the package that provides the Fastly Python build tool and gives you access to the Fastly API: -### Available Examples + `pip install fastly-compute` +2. Make a project shaped like [our Flask example](/examples/flask-app). You may find it easiest to clone the [repository](/), copy the `examples/flask-app` folder, and modify it. If you change the name of the top-level `.py` file, be sure to also update the entrypoint (`entry = "your_top_level_module_name"`) in `pyproject.toml`. +3. `cd your-project` +4. Install the [Fastly CLI](https://www.fastly.com/documentation/reference/tools/cli/) if you don't already have it. +5. `fastly compute init` +6. Say yes when warned "The current directory isn't empty." Answer "[4] Other" when it asks for Language. +7. Add this to the bottom of `fastly.toml`: + ``` + [scripts] + build = "fastly-compute-py build" + ``` -```bash -make list-examples # List all examples -make build-all # Build all examples -``` +8. `fastly compute build` +9. `fastly compute deploy` -### Testing -```bash -make test # Run integration tests -``` +## Run Some Examples on Your Own Machine -## Development - -### Build Tool Development - -The `fastly-compute-py` build tool is written in Rust. By default, the Makefile uses `cargo run` (DEV_MODE=1), which means: -- **No installation needed** - the tool runs directly via cargo -- **Always up-to-date** - changes to Rust code are automatically picked up -- **Fast incremental builds** - cargo handles recompilation efficiently - -Simply edit the Rust code in `crates/fastly-compute-py/` and run `make` - that's it! - -**Alternative: Using the Python Entry Point** - -To test the installed `fastly-compute-py` command (how end users will invoke it): -```bash -make DEV_MODE=0 # Uses `uv run fastly-compute-py` instead of `cargo run` -``` - -### Code Quality -```bash -make format # Format code (Python + Rust) -make lint # Run linter (Python + Rust) -make lint-fix # Auto-fix linting issues (Python + Rust) -``` - -### Building Examples -```bash -make build/my-app.wasm # Build specific example -make clean # Clean all build artifacts -``` +We ship [a few simple examples](examples/README.md) you can run locally to get a taste of what's possible. ## Status -Currently demonstrates: +Currently supports... - Building pure Python into WebAssembly components - Creating Python bindings from Fastly's WIT files - Hosting web frameworks by adapting Fastly's API to WSGI -- Comprehensive testing with viceroy integration +- Hosting non-WSGI applications by writing directly against Fastly's API ## Caveats -- Any native Python modules need to be compiled against WASI. Few are at the - moment. However, [Joel has done - some](https://github.com/dicej/wasi-wheels/releases/), and the changes needed - aren't extensive. +- So our memory-snapshotting build process can retain them, all the packages + needed at runtime must get imported when your entrypoint (e.g. `flask-app.py`) + is imported. This can happen transitively. But beware of deferred imports like + non-top-level ones; if they aren't triggered by importing your entrypoint, + they will fail at runtime. (If you have third-party code that uses + non-top-level imports, you can ensure they work by importing them at the top + level in your own code.) +- Third-party C extension modules are not yet supported. +- Our in-Python API may change backward-incompatibly during this beta period. ## Contributing diff --git a/examples/README.md b/examples/README.md index efbbf89..25e323c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,80 +1,49 @@ # Fastly Compute Python Examples -This directory contains example applications demonstrating different approaches to building Fastly Compute services with Python. +This directory contains small examples of Fastly Compute services built with Python. ## Available Examples -### `bottle-app.py` +### `bottle-app` - **Framework**: Bottle (lightweight WSGI framework) -- **Shows**: Basic routing, JSON responses, WIT API integration +- **Demonstrates**: Basic routing, JSON responses, WIT API integration - **Use Case**: Simple services, proof-of-concept applications -### `flask-app.py` +### `flask-app` - **Framework**: Flask (popular Python web framework) -- **Shows**: Flask routing, request handling, error handling +- **Demonstrates**: Flask routing, request handling, error handling - **Use Case**: More complex applications, familiar Flask patterns -### `game-of-life.py` -A server-side implementation of Conway’s Game of Life, with a server round trip per frame. - -- **Shows**: Raw requests-per-second performance; Fastly's session-reuse +### `game-of-life` +- **Shows**: A server-side implementation of Conway’s Game of Life, with a server round trip per frame. +- **Demonstrates**: Raw requests-per-second performance; Fastly's session-reuse feature, which saves spin-up time in busy services +### `backend-requests` +This is actually a piece of the test harness; please ignore it. + ## Building and Running Examples -### Build a Specific Example -```bash -make build/flask-app.wasm # Build Flask example -make build/bottle-app.wasm # Build Bottle example -make build/game-of-life.wasm # Build Conway's Game of Life example -``` +Before you use `make`, please [install the prerequisites](../CONTRIBUTING.md#prerequisites). -### Serve an Example +### Hello World in Flask or Bottle ```bash -make serve # Serve default (bottle-app) -make serve EXAMPLE=flask-app # Serve Flask example +make serve # Serve default example (Bottle) +make serve EXAMPLE=flask-app # Serve Flask example ``` -### Build All Examples -```bash -make build-all -``` +Visit http://127.0.0.1:7676/hello/world or http://127.0.0.1:7676/info. -### List Available Examples +### Conway's Game of Life ```bash -make list-examples +make serve EXAMPLE=game-of-life # Serve Conway's Game of Life example ``` -## Testing Examples +Visit http://127.0.0.1:7676/. -The integration tests use the default example (bottle-app). To test other examples: +### Other Invocations ```bash -# Update the test to use a different example -EXAMPLE=flask-app make test -``` - -## Creating New Examples - -1. Create a new `.py` file in this directory -2. Implement your WSGI application -3. Include the `serve_wsgi_request` function and `HttpIncoming` class -4. Build with `make build/your-example.wasm` -5. Test with `make serve EXAMPLE=your-example` - -## Framework Requirements - -All examples must: -- Be WSGI-compatible applications -- Include the WIT integration boilerplate (`serve_wsgi_request`, `HttpIncoming`) -- Handle the standard test endpoints for integration tests: - - `/hello/` - Returns "Hello {name}!" - - `/info` - Returns JSON with service info and WIT data - - `/error` - Raises an exception for error testing - -## Dependencies - -Examples may use different web frameworks, but they all rely on: -- `wit_world` - Generated WIT bindings -- `componentize-py` - Python to WebAssembly compilation -- Framework-specific dependencies (bottle, flask, etc.) \ No newline at end of file +make list-examples # List all examples +make build-all # Build all examples +``` diff --git a/examples/backend-requests/backend-requests.py b/examples/backend-requests/backend-requests.py index 6e92cb3..611d95b 100644 --- a/examples/backend-requests/backend-requests.py +++ b/examples/backend-requests/backend-requests.py @@ -7,7 +7,7 @@ from bottle import Bottle, request -import fastly_compute.requests as requests +from fastly_compute import requests from fastly_compute.wsgi import WsgiHttpIncoming app = Bottle()