Metadata-Version: 2.5
Name: fromscratch-pyweb
Version: 0.4.0
Summary: An educational HTTP web server built from scratch in Python
Project-URL: Homepage, https://github.com/robcox2910/pyweb
Project-URL: Repository, https://github.com/robcox2910/pyweb
Project-URL: Issues, https://github.com/robcox2910/pyweb/issues
Project-URL: Documentation, https://robcox2910.github.io/pyweb/
Author: Rob Cox
License: MIT
License-File: LICENSE
Keywords: education,http,learning,web-server
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Education
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Education
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: fromscratch-pycrypt>=0.2.0
Requires-Dist: fromscratch-pynet>=0.2.0
Provides-Extra: dev
Requires-Dist: commitizen>=4.4.0; extra == 'dev'
Requires-Dist: hypothesis>=6.170; extra == 'dev'
Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
Requires-Dist: pre-commit>=4.0.1; extra == 'dev'
Requires-Dist: pyright>=1.1.400; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.3.1; extra == 'dev'
Requires-Dist: pytest>=8.3.4; extra == 'dev'
Requires-Dist: ruff>=0.9.3; extra == 'dev'
Description-Content-Type: text/markdown

# PyWeb

An educational HTTP web server built from scratch in Python.

Every time you visit a website, your browser sends a letter to a server
and gets a reply. PyWeb is that server -- built from scratch so you can
see exactly how it works. Think of it as a post office: letters arrive,
get sorted, and replies are sent back.

## Features

- **HTTP request parsing** -- read the incoming letter (method, path, headers, body, query params)
- **Response building** -- write the reply (status codes, headers, body)
- **URL routing** -- the mailroom that sorts letters to the right handler
- **Path parameters** -- dynamic URLs like `/users/<id>`
- **Template engine** -- form letters with `{{ blanks }}` to fill in
- **Static files** -- serve CSS, JS, images straight from a folder
- **JSON support** -- parse JSON request bodies, return JSON responses
- **Logging** -- see every request and response in the terminal
- **405 handling** -- proper "Method Not Allowed" for wrong HTTP verbs

## Example

```python
from pyweb.router import Router
from pyweb.response import html_response, json_response
from pyweb.server import Server
from pyweb.template import render

router = Router()

@router.get("/")
def homepage(request):
    return html_response(render(
        "<h1>Hello, {{ name }}!</h1>",
        {"name": "World"}
    ))

@router.get("/users/<id>")
def get_user(request):
    return json_response({"user_id": request.params["id"]})

Server(router).serve_forever()
```

## Quick Start

```bash
# Install dependencies
uv sync --all-extras

# Run the example server
uv run python examples/hello_server.py
# Visit http://127.0.0.1:8000

# Run tests
uv run pytest
```

## Concepts

Every concept is explained with real-world analogies:

| Concept | Doc | Analogy |
|---------|-----|---------|
| Requests | [requests.md](docs/concepts/requests.md) | The incoming letter |
| Responses | [responses.md](docs/concepts/responses.md) | The reply letter |
| Routing | [routing.md](docs/concepts/routing.md) | The mailroom |
| Templates | [templates.md](docs/concepts/templates.md) | Form letters with blanks |
| Server | [server.md](docs/concepts/server.md) | The post office |

<!-- docs:start -->
## Documentation

Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pyweb/
<!-- docs:end -->

## Async PyWeb

PyWeb also has an async framework that runs on [PyNet](https://github.com/robcox2910/pynet)'s
own event loop: one shopkeeper serving many customers at once, with
server-sent events, WebSockets, sessions and roles.

```python
from pynet.loop import run
from pynet.streams import open_connection

from pyweb.app import App
from pyweb.aserver import AsyncServer

app = App()


@app.get("/hello/<name>")
async def hello(request, name):
    return {"greeting": f"Hello, {name}!"}


async def main():
    async with AsyncServer(app, access_log=False) as server:
        reader, writer = await open_connection("127.0.0.1", server.port)
        writer.write(b"GET /hello/Sam HTTP/1.1\r\nConnection: close\r\n\r\n")
        await writer.drain()
        print((await reader.read()).split(b"\r\n\r\n")[1].decode())
        writer.close()


run(main())
```

Output: `{"greeting": "Hello, Sam!"}`

Run the live demo (a page, a clock that ticks every second, and a
WebSocket echo) and visit http://127.0.0.1:8000, then press Ctrl-C to stop:

```bash
uv run pyweb --async
```

More programs live in `examples/`: `async_hello.py`, `sse_clock.py`,
`ws_echo_page.py` and `login_demo.py`. The step-by-step lessons start at
"Part 2: A Busy Restaurant" in [docs/index.md](docs/index.md).

### Developing alongside a sibling

PyWeb uses PyNet and PyCrypt from PyPI (`fromscratch-pynet` and
`fromscratch-pycrypt`). To work on a sibling
checkout at the same time, install it editable and tell `uv` not to
re-sync over it:

```bash
uv pip install -e ../pynet
uv run --no-sync pytest
```

## Releasing

Other projects in the series ([PyStack](https://github.com/robcox2910/pystack)) use a pinned release of this one,
not whatever is on `main`. To publish a change for them:

1. Merge the change to `main`.
2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
   A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
   anything else bumps the patch (`v0.1.0` → `v0.1.1`).
3. In each project that uses it, edit the tag in `[tool.uv.sources]`, run
   `uv lock --upgrade-package <name>`, and open a pull request so CI checks
   the pair together.

<!-- series:start -->
## Related Projects

This project is one of a series that builds real systems "from scratch in Python" to teach how they work. See the [series home](https://robcox2910.github.io/) for the full story.

| Project | What it teaches | Repository | Docs |
| --- | --- | --- | --- |
| PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
| Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
| PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
| PyKV | Key-value stores | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
| PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
| PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
| PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
| PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
| PyColDB | Column-family databases | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
| PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
| PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
| **PyWeb** | **HTTP web servers** | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
| PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
| PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
| PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
| PyLLM | Language models (the brain) | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
| PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
| PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
<!-- series:end -->

## License

MIT
