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
Metadata
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 | Provenance | Actions |
|---|---|---|---|---|
| lessweb-2.2.0.tar.gz | 47.2 kB | Without provenance | Details for lessweb-2.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lessweb-2.2.0-py3-none-any.whl | Python 3 | none | any | Details for lessweb-2.2.0-py3-none-any.whl |
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
|
Security for lessweb 2.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.