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

$ pip install lcore

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.

TestLcoreFlask 3.1Bottle 0.13Lcore vs Flask
Plaintext122,71530,500193,3684.0x
JSON95,91026,403137,0703.6x
Route Params90,33224,784126,4653.6x
Middleware (2 layers)53,11324,261117,3482.2x
404 Miss37,19116,80244,6512.2x
Multi-Route (50 routes)96,81227,488145,5953.5x
POST JSON62,96321,14995,5443.0x
Plaintext (req/s)
Lcore
122,715
Bottle
193,368
Flask
30,500
JSON (req/s)
Lcore
95,910
Bottle
137,070
Flask
26,403
Middleware Stack (req/s)
Lcore
53,113
Bottle
117,348
Flask
24,261

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.

FeatureLcoreFlaskBottleFastAPI
Single file / zero depsYesNoYesNo
WSGIYesYesYesNo
ASGINoNoNoYes
Async route handlers Yes Yes (2.0+)NoYes
Typed route parametersYesYesYesYes
Custom route filtersYesYesYesNo
Route groupsYesBlueprintsNoAPIRouter
Built-in middleware7 included00Several
CORS middlewareBuilt-inExtensionPluginBuilt-in
CSRF protectionBuilt-inExtensionNoNo
Security headersBuilt-inExtensionNoNo
Rate limitingBuilt-in (per-process; pass backend= for shared)ExtensionNoExtension
Request validationBuilt-inExtensionNoBuilt-in
Dependency injectionBuilt-in (3 lifetimes)NoNoBuilt-in
Plugin systemYesExtensionsYesNo
Lifecycle hooks12 hook pointsSignals6 hooksEvents
Module mountingYes (WSGI)BlueprintsYesSub-apps
Template engines4 enginesJinja2SimpleTemplateJinja2
Signed cookiesHMAC-SHA256itsdangerousHMACNo
Static file servingYes (ETag, Range)YesYesYes
Server adapters21 adaptersWerkzeug20+ adaptersUvicorn
Hot reloadBuilt-in + WatchdogWerkzeugYesUvicorn
CLI interfaceYesYesNoNo
Auto API docs (HTML + JSON)Built-inNoNoOpenAPI/Swagger
Body size limitsBuilt-inConfigConfigConfig
Gzip compressionBuilt-inExtensionNoMiddleware
Request ID trackingBuilt-inExtensionNoExtension
Password hashingPBKDF2-SHA256ExtensionNoNo
Test clientBuilt-inBuilt-inNoBuilt-in
Background tasksBuilt-inCeleryNoBuilt-in
Graceful shutdownBuilt-inServerNoServer

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:

SituationBetter choice
You need WebSockets or real-time async I/OFastAPI, 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 benefitStay on Flask
You need automatic OpenAPI / Swagger generationFastAPI
You need ASGI and Uvicorn / Daphne todayFastAPI, Starlette
Your workload is I/O-bound and you want event-loop concurrencyFastAPI, Starlette
You need a full MVC framework with ORM, admin panel, and migrationsDjango
Lcore is a good fit when...

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)