lessweb
Lessweb is a ready-to-use, production-grade Python web framework with the following goals:
- Ready-to-use: Easily parse configuration files, set up logging with ease, and dynamically override configuration items using environ variables.
- Production-grade: Built on the aiohttp ecosystem and boasting powerful IOC capabilities.
- Pythonic: Supports the latest Python versions and syntax.
📦 Installation
To install the latest version of lessweb for Python ≥ 3.10, run:
pip install lessweb
Dependencies
⚡ Quick Start Example
Save the following to main.py:
from typing import Annotated
from lessweb import Bridge
from lessweb.annotation import Get
async def hello(*, who: str = 'world') -> Annotated[dict, Get('/')]:
return {'message': f'Hello, {who}!'}
def main():
bridge = Bridge()
bridge.scan(hello)
bridge.run_app()
if __name__ == '__main__':
main()
Run your app:
python main.py
Now open your browser at http://localhost:8080
Output:
{"message": "Hello, world!"}
Or try http://127.0.0.1:8080?who=John:
{"message": "Hello, John!"}
📚 Official Documentation
- Quick Start: https://lessweb.castdict.com/quickstart/
- IOC / Dependency Injection: https://lessweb.castdict.com/ioc/
- Reference Manual: https://lessweb.castdict.com/reference/
📘 Lessweb Framework Guide
Below is a summarized version of the Quick Start and IOC (Dependency Injection) documentation.
🧭 Endpoint Basics
An endpoint in lessweb is an async function bound to a specific HTTP route (URL). Endpoints can automatically receive parameters from query strings, path variables, or the request body.
Example:
from typing import Annotated
from aiohttp.web import Request
from lessweb import Bridge
from lessweb.annotation import Get, Post
async def get_pet_detail(request: Request) -> Annotated[dict, Get('/pet/{pet_id}')]:
return {'pet_id': request.match_info['pet_id']}
async def create_pet(request: Request) -> Annotated[dict, Post('/pet')]:
pet = await request.json()
return pet
if __name__ == '__main__':
bridge = Bridge()
bridge.scan(get_pet_detail, create_pet)
bridge.run_app()
You can also scan an entire package:
bridge.scan('myapp.endpoint')
Dynamic Path Parameters
Supports regex-based path matching:
async def get_pet_detail(request: Request) -> Annotated[dict, Get('/pet/{pet_id:[0-9]+}')]:
...
🧩 Request Parameter Injection
Lessweb automatically injects request data into endpoint parameters:
- keyword-only parameters (
*, name: str) → path/query parameters - positional-only parameters (
data: Model, /) → JSON body - normal parameters (
request: Request) → context objects or services
Example:
from pydantic import BaseModel
from lessweb.annotation import Get, Post
from typing import Annotated
class Pet(BaseModel):
pet_id: int
name: str
async def get_pet_detail(*, pet_id: int) -> Annotated[dict, Get('/pet/{pet_id}')]:
return {'pet_id': pet_id}
async def create_pet(pet: Pet, /) -> Annotated[dict, Post('/pet')]:
return pet
Supported types include:
str,int,float,bool,listdatetime,date,timeenum,Literal,Union,NewTypepydantic.BaseModel
💡 JSON Responses
Endpoints can directly return JSON-compatible data:
async def get_pet_list() -> Annotated[dict, Get('/pet')]:
return [{'pet_id': 1, 'name': 'Kitty'}]
Or use helper functions for advanced responses:
from lessweb import rest_response, rest_error
from aiohttp.web import HTTPBadRequest
async def get_pet_list() -> Annotated[dict, Get('/pet')]:
return rest_response([{'name': 'Kitty'}], headers={'X-TOKEN': 'abc123'})
async def bad_request() -> Annotated[dict, Get('/error')]:
raise rest_error(HTTPBadRequest, {'code': -1, 'message': 'Invalid request'})
⚙️ JSON Serialization
Based on orjson with support for:
- dataclasses
- datetime / date / time (RFC 3339)
- enum
- numpy types
- uuid
- pydantic models
Configurable in config.toml:
[lessweb]
orjson_option = 'APPEND_NEWLINE,INDENT_2,UTC_Z'
🧠 Dependency Injection (IOC)
Lessweb’s IOC system auto-injects dependencies based on parameter types, similar to Spring Boot.
Lifecycle Levels
| Type | Scope | Description |
|---|---|---|
| Module | Process-level singleton | e.g. DB connection pool |
| Middleware | Request-level wrapper | Pre/post processing |
| Service | Request-level singleton | Business logic |
| Bean | Request-level factory result | Complex object creation |
Example: Modules
class Mysql(Module):
async def on_startup(self, app):
self.pool = await aiomysql.create_pool(...)
class RedisModule(Module):
async def on_startup(self, app):
self.redis_client = redis.Redis(...)
Example: Middleware
class MysqlConn(Middleware):
def __init__(self, mysql: Mysql):
self.mysql = mysql
async def on_request(self, request, handler):
async with self.mysql.pool.acquire() as conn:
self.conn = conn
return await handler(request)
Example: Services and Beans
class TaskService(Service):
def __init__(self, dao: Commondao, redis: redis.Redis):
self.dao = dao
self.redis = redis
def commondao_bean(mysqlConn: MysqlConn) -> Commondao:
return Commondao(mysqlConn.conn, mysqlConn.cur)
def redis_bean(redis_module: RedisModule) -> redis.Redis:
return redis_module.redis_client
Register all in main.py:
def main():
bridge = Bridge()
bridge.beans(commondao_bean, redis_bean)
bridge.middlewares(MysqlConn)
bridge.scan('src')
bridge.run_app()
✅ Best Practices
- Use Middleware for request pre/post hooks (e.g. auth, logging)
- Use Service for business logic
- Keep Module dependencies only between other Modules
- Let Beans create reusable request-scoped objects
📄 License
Lessweb is offered under the Apache 2.0 License.
🧭 Source Code
GitHub Repository: 👉 https://github.com/lessweb/lessweb
Release files for lessweb 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lessweb-2.2.0.tar.gz | 47.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lessweb-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.7 kB
Release files / lessweb-2.2.0.tar.gz
| Download URL | lessweb-2.2.0.tar.gz |
|---|---|
| Size | 47.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1bff733630e4f5186659bab59d66466eeefd53511cc2ff495f75e19c2774686d
|
|
BLAKE2b-256 checksum How to use checksums |
d6d9f78a80d32f6a23df0a6cf4d9359e2ae6f7882e3f6cd4701287422961a344
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.8
|
Release files / lessweb-2.2.0-py3-none-any.whl
| Download URL | lessweb-2.2.0-py3-none-any.whl |
|---|---|
| Size | 22.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a04c937cb5590b1817fc0dc57ce30589bad75fe2d5b1fbc0ec261da078d78aa7
|
|
BLAKE2b-256 checksum How to use checksums |
65097661e29330b29e98b74c479e1c750a082550e1fa40fb12dd529915787ae1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.8
|