Build Microservices
at Lightning Speed
A fast, lightweight, single-file Python WSGI framework. Zero dependencies. Production-grade. One file. async def supported with caveats ⚠
Python 3.8+ | v0.0.1 | Early Release | MIT | Zero Dependencies
Quick Install
Zero dependencies — only Python 3.8+ required. Every import is from the standard library. No pip install needed beyond Lcore itself.
Hello World
from lcore import Lcore
app = Lcore()
@app.route('/hello')
def hello():
return {'message': 'Hello, World!'}
app.run(host='0.0.0.0', port=8080)
Performance
WSGI throughput benchmark — 100,000 iterations × 3 runs, best of 3. Measures pure framework overhead.
| Test | Lcore | Flask 3.1 | Bottle 0.13 | Lcore vs Flask |
|---|---|---|---|---|
| Plaintext | 122,715 | 30,500 | 193,368 | 4.0x |
| JSON | 95,910 | 26,403 | 137,070 | 3.6x |
| Route Params | 90,332 | 24,784 | 126,465 | 3.6x |
| Middleware (2 layers) | 53,113 | 24,261 | 117,348 | 2.2x |
| 404 Miss | 37,191 | 16,802 | 44,651 | 2.2x |
| Multi-Route (50 routes) | 96,812 | 27,488 | 145,595 | 3.5x |
| POST JSON | 62,963 | 21,149 | 95,544 | 3.0x |
Lcore is 2.2x – 4.0x faster than Flask across all 7 test scenarios. Bottle is faster still but lacks middleware, DI, security stack, and lifecycle hooks that Lcore provides out of the box.
Feature Comparison
How Lcore compares to other Python microframeworks.
| Feature | Lcore | Flask | Bottle | FastAPI |
|---|---|---|---|---|
| Single file / zero deps | Yes | No | Yes | No |
| WSGI | Yes | Yes | Yes | No |
| ASGI | No | No | No | Yes |
| Async route handlers | Yes ⚠ | Yes (2.0+) | No | Yes |
| Typed route parameters | Yes | Yes | Yes | Yes |
| Custom route filters | Yes | Yes | Yes | No |
| Route groups | Yes | Blueprints | No | APIRouter |
| Built-in middleware | 7 included | 0 | 0 | Several |
| CORS middleware | Built-in | Extension | Plugin | Built-in |
| CSRF protection | Built-in | Extension | No | No |
| Security headers | Built-in | Extension | No | No |
| Rate limiting | Built-in (per-process; pass backend= for shared) | Extension | No | Extension |
| Request validation | Built-in | Extension | No | Built-in |
| Dependency injection | Built-in (3 lifetimes) | No | No | Built-in |
| Plugin system | Yes | Extensions | Yes | No |
| Lifecycle hooks | 12 hook points | Signals | 6 hooks | Events |
| Module mounting | Yes (WSGI) | Blueprints | Yes | Sub-apps |
| Template engines | 4 engines | Jinja2 | SimpleTemplate | Jinja2 |
| Signed cookies | HMAC-SHA256 | itsdangerous | HMAC | No |
| Static file serving | Yes (ETag, Range) | Yes | Yes | Yes |
| Server adapters | 21 adapters | Werkzeug | 20+ adapters | Uvicorn |
| Hot reload | Built-in + Watchdog | Werkzeug | Yes | Uvicorn |
| CLI interface | Yes | Yes | No | No |
| Auto API docs (HTML + JSON) | Built-in | No | No | OpenAPI/Swagger |
| Body size limits | Built-in | Config | Config | Config |
| Gzip compression | Built-in | Extension | No | Middleware |
| Request ID tracking | Built-in | Extension | No | Extension |
| Password hashing | PBKDF2-SHA256 | Extension | No | No |
| Test client | Built-in | Built-in | No | Built-in |
| Background tasks | Built-in | Celery | No | Built-in |
| Graceful shutdown | Built-in | Server | No | Server |
Why Lcore Exists
Most Python frameworks make one of two trade-offs: simple and fast (Bottle) but with no middleware, no DI, and no security stack -- or full-featured (Flask, Django) but dependent on many extensions and slower under load. Lcore closes that gap.
Bottle fills the speed and simplicity slot
Single file, zero deps, fast. But no middleware stack, no dependency injection, no security primitives, no lifecycle hooks. You build all of that yourself.
Flask fills the familiarity slot
Popular and well-documented. But you need flask-cors, flask-limiter, flask-login, and many more before you have a production API -- and each is a dependency.
Lcore fills the gap in between
Single file, zero deps, 3 to 4x faster than Flask, with CORS, CSRF, security headers, rate limiting, PBKDF2 hashing, DI, 12 lifecycle hooks, and a test client all built in. Nothing to install beyond Lcore itself.
Design Philosophy
One file only
The entire framework is lcore.py. No package structure, no sub-modules, no build step. Drop it into any project and import it.
Zero external dependencies
Every import is from Python's standard library. Works in air-gapped environments, containers, and serverless functions where pip may not be available.
Production features built in, not bolted on
CORS, CSRF, security headers, rate limiting, signed cookies, and password hashing ship with the framework. You should not need five extensions to secure a production API.
Honest about limitations
Lcore is WSGI. It documents and warns loudly when a feature has constraints -- async handlers, per-process rate limiting, thread-blocking timeouts. No surprises.
Standard Python throughout
No metaclass magic, no descriptor abuse, no import-time side effects beyond what is declared. The source is meant to be read and understood.
449 tests, zero test dependencies
The test suite uses only unittest from the standard library. Run it anywhere with python -m pytest tests/ after installing pytest.
When NOT to use Lcore
Lcore is a strong fit for synchronous WSGI workloads. It is the wrong choice in these situations:
| Situation | Better choice |
|---|---|
| You need WebSockets or real-time async I/O | FastAPI, Starlette, Quart |
| You need true async concurrency (async DB drivers, hundreds of simultaneous outbound calls) | FastAPI, Starlette |
| Your team is already on Flask and migration cost outweighs any benefit | Stay on Flask |
| You need automatic OpenAPI / Swagger generation | FastAPI |
| You need ASGI and Uvicorn / Daphne today | FastAPI, Starlette |
| Your workload is I/O-bound and you want event-loop concurrency | FastAPI, Starlette |
| You need a full MVC framework with ORM, admin panel, and migrations | Django |
Your workload is primarily synchronous: REST APIs, internal services, background job APIs, or microservices that talk to SQL databases via sync drivers (psycopg2, sqlite3, mysqlclient). If you want maximum feature density in a single file with no installation ceremony, Lcore is built for that.
Ideal Use Cases
Internal REST APIs
Admin panels, dashboards, and data pipelines that talk to a SQL database via psycopg2, sqlite3, or mysqlclient. No async I/O needed.
Microservices with No Async I/O
Services that call other services synchronously, process files, hash passwords, or do CPU-bound work. Straightforward WSGI with a full feature set.
Rapid Prototyping
Drop in a single file, no virtual environment ceremony, start handling requests in minutes. No extensions to install before you have auth, CORS, and rate limiting.
Air-Gapped or Restricted Environments
Zero external dependencies means no supply-chain risk and no pip access required. Works anywhere Python 3.8+ runs.
Embedded and Constrained Deployments
Containers, serverless functions, or edge nodes where you want the smallest possible footprint. One file, no transitive dependencies.
Teams Moving Off Bottle
Familiar single-file philosophy with the middleware, DI, security, and lifecycle hooks that Bottle leaves you to build yourself.
Scripts That Occasionally Serve HTTP
Background workers, CLI tools, or data jobs that also expose a health check or webhook endpoint. No separate web framework install needed.
Webhook Receivers and Event Processors
Ingest webhooks from Stripe, GitHub, Slack, or any third-party service. Validate HMAC signatures, parse JSON, and dispatch to handlers -- all with built-in security primitives.
Learning Python Web Development
A single readable file with no magic, no metaclasses, and no hidden layers. Ideal for understanding how WSGI, routing, middleware, and request/response cycles actually work.
Key Features
Single File, Zero Dependencies
The entire framework is one Python file built on Python's standard library only. No third-party packages required. Works anywhere Python runs — containers, serverless, or air-gapped networks.
Powerful Routing
Typed parameters, wildcard filters, method decorators, and route groups. async def handlers are accepted with automatic wrapping — see the async caveat.
Security First
CORS, CSRF, security headers, signed cookies, PBKDF2 password hashing, body limits, rate limiting, and timing-safe comparison built in.
7 Built-in Middleware
CORS, CSRF, security headers, compression, body limits, request ID, and structured logging out of the box.
Dependency Injection
Register singleton, scoped, or transient dependencies. Auto-inject DB sessions, caches, and loggers per-request.
Async/Await WSGI Limitation
Lcore accepts async def route handlers, but the worker thread is blocked
for the full duration. No concurrency benefit.
Full details
12 Lifecycle Hooks
Fine-grained control at every stage: request start, auth, handler enter/exit, response build/send, and more.
21 Server Adapters
Gunicorn, Waitress, Gevent, Eventlet, Tornado, aiohttp, Bjoern, Meinheld, and more. Use server='auto'.
Plugin Architecture
JSON serialization, template rendering, and custom plugins with setup/apply lifecycle. Per-route skip support.
Module Mounting
Mount sub-applications with isolated routes, hooks, and middleware. Compose microservices into larger systems.
Built-in Test Client
Test routes without starting a server. TestClient calls WSGI directly — assert status codes, JSON bodies, and headers in unit tests.
Background Tasks
Fire-and-forget work with BackgroundTaskPool. Send emails, run cleanup, or process analytics without blocking the response.
Production Microservice
from lcore import (
Lcore, request, response, ctx,
CORSMiddleware, SecurityHeadersMiddleware,
RequestIDMiddleware, BodyLimitMiddleware,
rate_limit, validate_request, on_shutdown
)
app = Lcore()
# Production middleware stack
app.use(RequestIDMiddleware())
app.use(SecurityHeadersMiddleware(hsts=True))
app.use(CORSMiddleware(allow_origins=['https://myapp.com']))
app.use(BodyLimitMiddleware(max_size=5 * 1024 * 1024))
# Dependency injection
app.inject('db', create_db_session, lifetime='scoped')
app.inject('cache', RedisCache, lifetime='singleton')
# Rate-limited, validated API endpoint
@app.route('/api/users/<id:int>', method='GET')
@rate_limit(100, per=60)
def get_user(id):
user = ctx.db.query(User).get(id)
if not user:
abort(404, 'User not found')
return user.to_dict()
@app.post('/api/users')
@validate_request(body={'name': str, 'email': str})
def create_user():
data = request.json
return {'id': ctx.db.insert(data), 'created': True}
# Graceful shutdown
@on_shutdown
def cleanup():
close_all_connections()
app.run(server='gunicorn', host='0.0.0.0', port=8080)
Lcore