2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00
2026-06-02 16:26:10 +08:00

┌─────────────────────────────────────────────────┐
│                                                 │
│     ██████╗  █████╗ ███████╗████████╗███████╗   │
│     ██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔════╝   │
│     ██████╔╝███████║███████╗   ██║   █████╗     │
│     ██╔═══╝ ██╔══██║╚════██║   ██║   ██╔══╝     │
│     ██║     ██║  ██║███████║   ██║   ███████╗   │
│     ╚═╝     ╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝   │
│                                                 │
│      Python Api-first Scalable Task Engine      │
│                                                 │
└─────────────────────────────────────────────────┘

PASTE — A Production-Ready Lightweight Python Framework

A minimalist, battle-tested Python framework for building api services with built-in RBAC, JWT, async tasks, Snowflake IDs, Swagger, and modular utilities — zero boilerplate, maximum control. Built on top of Tornado.

License Python Build


Core Features

Feature Description
🚀 Auto-Loading Handlers Define route_pattern = "/user" in your handler → API is automatically registered. No router config needed.
🔐 RBAC with Dynamic Rules Permissions stored as serialized Python objects in DB. Define complex rules as classes (e.g., TimeBasedRule, IPWhitelistRule).
📜 Swagger UI Auto-Generated Built-in Swagger UI at /docs. No YAML. Schema is inferred from handlers and decorators.
🔢 Snowflake ID Generator Embedded, thread-safe, no external dependency. Generates 10K+ unique IDs/sec.
Async Task Pool with Backpressure run_background_task(coro) safely manages concurrent background jobs with configurable limits.
🔐 JWT + Password Hashing Stateless authentication via paste.security.token. Passwords hashed with PBKDF2-sha256.
🧰 Modular Utility Library (util/) Clean, testable utilities: ustr, udict, ufile, pagination, snow_id, encoder — no pollution in __init__.py.
📦 Layered Architecture core/ (config, logging, async), db/ (engine, models), rbac/ (roles, rules), web/ (handlers, app), service/ (business logic) — clean separation.
🛡️ Safe File Handling sanitize_filename() ensures cross-platform compatibility (Windows/Linux/macOS).
📊 JSON Encoder for Complex Types Auto-serializes datetime, Decimal, BaseModel, Enum, numpy — no extra config.
📬 Redis Stream Actor Reliable consumer-group-based message processing with auto zombie-task recovery.
🧩 ParamAware UI Modules Async-preload data for Tornado UIModules — solves the age-old sync-render bottleneck.

🛠️ Quick Start

1. Install

git clone https://github.com/your-repo/paste.git
cd paste
pip install -e .

2. Run the example

All ready-to-run examples are located in the examples/ directory:

cd examples/01_hello_world
python main.py

👉 Open: http://localhost:9090/hello


📚 Examples

Jump right in with these working examples:

Example Description Key Features Demonstrated
01_hello_world Minimal paste app Auto-loading handlers, config, response helpers
02_background_task Async background tasks Task pool, logging, daemonized services
03_redis_stream Redis Stream publisher & consumer StreamActor, consumer groups, message ACK
04_tasks_service Scheduled task service TaskService, cron-like scheduling, PID management
05_gen_models Auto-generate DB models from existing tables SQLAlchemy model generation

Each example includes a config.json, a handler.py, and a main.py — ready to run.


🏗️ Framework Architecture

paste/                          # Framework core (do NOT modify)
├── core/                       # Foundation layer
│   ├── config.py               #    Dot-path config loader (get_config("db.engine"))
│   ├── logging.py              #    Logger with rotating file + console
│   └── aio_pool.py             #    Async task pool with backpressure
├── db/                         # Database layer
│   ├── engine.py               #    SQLAlchemy engine factory
│   ├── redis.py                #    Redis connection + StreamActor
│   ├── basemodel.py            #    Async ORM base model
│   ├── basetable.py            #    Table reflection utilities
│   ├── baseadapter.py          #    Result-set adapter
│   └── gen_models.py           #    Auto-generate model classes
├── web/                        # Web layer
│   ├── application.py          #    Tornado Application with auto-loading
│   ├── handler.py              #    Base RequestHandler with response helpers
│   ├── decorators.py           #    @route, @auth_token, @auth_permission
│   ├── swagger.py              #    Swagger UI auto-generation
│   ├── form.py                 #    WTForms integration
│   ├── param_aware_loader.py   #    Async UIModule data preloader
│   └── websocket.py            #    WebSocket support
├── rbac/                       # Access control layer
│   ├── rbac_user.py            #    User with permission inheritance
│   ├── rbac_role.py            #    Role management
│   ├── rbac_rule.py            #    Rule engine (pickle-serialized)
│   ├── rbac_item.py            #    Permission item hierarchy
│   ├── rbac_assignment.py      #    User-item assignment
│   └── rbac_permission.py      #    Permission query
├── security/                   # Security layer
│   ├── token.py                #    JWT encode/decode with configurable issuer
│   └── shash.py                #    PBKDF2-sha256 password hashing
├── service/                    # Service layer
│   ├── server.py               #    Server process management
│   ├── daemonize.py            #    Unix daemon + PID file
│   └── task_service.py         #    Scheduled task runner
├── chart/                      # Chart generation (optional)
│   ├── bar.py / pie.py / line.py
│   └── ...
└── util/                       # Utility library
    ├── ustr.py / udict.py      #    String & dict helpers
    ├── ufile.py                #    File operations with sanitized filenames
    ├── pagination.py           #    Page, Pagination, CursorPagination
    ├── snow_id.py              #    Snowflake ID generator
    ├── encoder.py              #    JSON encoder + BaseX decoder
    ├── pdf.py / svg.py / xlsx.py
    └── ...

Your application code lives entirely outside paste/:

myapp/
├── main.py                  # Entry point: create Application, listen, start
├── config.json              # DB, logger, RBAC, Tornado configuration
├── apps/                    # Your handlers (recommended)
│   └── demo/
│       ├── __init__.py
│       ├── handler_user.py
│       └── handler_auth.py
├── models/                  # Your DB models
│   └── db_models.py
└── service/                 # Your background services
    └── __init__.py

🔐 RBAC Example

# handler.py
from paste.rbac.rbac_user import RbacUser
from paste.web.decorators import route, auth_token, auth_permission
from paste.web.handler import RequestHandler

@route("/admin/users")
class AdminUserHandler(RequestHandler):
    @auth_token
    @auth_permission
    async def get(self):
        users = await RbacUser.query_as_df(RbacUser().gen_query())
        self.response_ok(rows=users.to_dict('records'))
# rule.py — dynamic rule as a serialized Python class
from datetime import datetime
from paste.rbac.rbac_rule import RbacRule

class BusinessTimeRule(RbacRule):
    async def run(self, **kwargs) -> bool:
        hour = datetime.now().hour
        return 9 <= hour < 18  # only allow during business hours

⚙️ Configuration

All configuration lives in a single config.json:

{
  "tornado": {
    "demo": {
      "autoreload": false,
      "handlers_pkg": "apps.demo",
      "port": 9090,
      "static_path": "static",
      "template_path": "templates",
      "swagger_title": "DemoAPI",
      "swagger_description": "Demo API",
      "swagger_api_version": "1.0.1"
    }
  },
  "db": {
    "engine": {
      "engine": "mysql+pymysql://user:pass@localhost:3306/mydb",
      "async_engine": "mysql+aiomysql://user:pass@localhost:3306/mydb",
      "engine_option": {
        "echo": false,
        "pool_size": 10
      }
    }
  },
  "redis": {
    "connection": "redis://localhost:6379/0",
    "streams": {
      "user_event": {
        "group": "user_event_group",
        "consumer": "processor_01"
      }
    }
  },
  "rbac": {
    "user_class": "myapp.models.MyRbacUser",
    "table": {
      "rule": "rbac_rule",
      "user": "rbac_user",
      "item": "rbac_item",
      "assignment": "rbac_assignment",
      "item_child": "rbac_item_child"
    }
  },
  "logger": {
    "default": {
      "basic": {
        "filename": "logs/app.log",
        "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        "level": 20
      }
    }
  }
}

Access any config value with dot-path:

from paste.core import config
engine_url = config.get_config("db.engine.engine")
port = config.get_config("tornado.demo.port", 9000)

📣 License

MIT © 2025 [Wayne Zhang / Organization]


🌍 Contributing

Please see CONTRIBUTING.md for details on:

  • Code style (Black + flake8 + mypy)
  • How to submit issues and pull requests

🧪 Running Tests

pytest                          # Run all tests
pytest tests/unit               # Run unit tests only (no external services)
pytest tests/integration        # Run integration tests (require DB/Redis)
pytest --cov=paste              # Run with coverage report

💡 Why paste?

Most frameworks make you write boilerplate. paste makes you stop writing boilerplate — and start building features.

  • No @app.get("/user") decorators — just route_pattern = "/user"
  • No YAML for Swagger — schema inferred automatically
  • No Redis for IDs — Snowflake built-in
  • No Celery for tasks — async pool with backpressure
  • No config files in 5 places — one config.json, one get_config("db.engine")
S
Description
A minimalist, battle-tested Python framework for building api services with built-in RBAC, JWT, async tasks, Snowflake IDs, Swagger, and modular utilities — zero boilerplate, maximum control. Built on top of Tornado.
Readme MIT 287 KiB
Languages
Python 100%