Skip to main content

๐ŸฆŠ Fennec - A lightweight, fast, and agile Python backend framework built in Tunisia

Project description

Fennec Framework

Fennec ๐ŸฆŠ

A lightweight, fast, and agile Python backend framework

Named after the Fennec fox - small, swift, and adaptable

PyPI version Python 3.8+ License: MIT

Built with โค๏ธ in Tunisia ๐Ÿ‡น๐Ÿ‡ณ


๐Ÿš€ Quick Start

# Install Fennec
pip install fennec-framework

# Create your first API
from fennec import Application

app = Application(title="My API")

@app.get("/")
async def hello():
    return {"message": "Hello from Fennec! ๐ŸฆŠ"}

# Run with: uvicorn main:app --reload

โœจ Features

A modular, async-first Python framework for building REST APIs with minimal dependencies.

Core Features

Core Features

  • ๐Ÿš€ Async/Await Support: Built from the ground up with async/await
  • ๐Ÿ”Œ Modular Architecture: Use only what you need
  • โœ… Automatic Validation: Type-hint based validation
  • ๐Ÿ“ Auto Documentation: OpenAPI/Swagger generation
  • ๐Ÿงช Testing Utilities: Easy-to-use test client
  • ๐Ÿ› ๏ธ CLI Tools: Project scaffolding and management commands
  • ๐Ÿ’‰ Dependency Injection: Clean dependency management
  • ๐ŸŽฏ Middleware System: Flexible request/response processing
  • ๐Ÿ–ฅ๏ธ GUI Client: Beautiful Tkinter desktop application included!

v0.2.0 Features

  • ๐Ÿ”Œ WebSocket Support: Real-time bidirectional communication
  • ๐Ÿ“Š GraphQL Support: Flexible API queries with GraphiQL interface
  • ๐Ÿ”’ Enhanced Security: Password hashing, CSRF protection, input sanitization
  • ๐Ÿ—„๏ธ Database Integration: PostgreSQL and MongoDB examples
  • ๐Ÿ—๏ธ Microservices: API Gateway pattern and service decomposition
  • ๐Ÿ” OAuth2 Authentication: Complete authorization code flow
  • ๐Ÿณ Docker Ready: Production deployment examples

v0.3.0 Features (Production-Grade)

  • ๐Ÿš€ Redis Caching: High-performance caching with decorators and strategies
  • ๐Ÿ—„๏ธ Database Migrations: Version-controlled schema management
  • ๐Ÿ“Š Monitoring Tools: Prometheus metrics, distributed tracing, structured logging
  • ๐ŸŽ›๏ธ Admin Dashboard: Real-time monitoring and management interface
  • ๐Ÿ“จ Message Queues: Async task processing with Redis, RabbitMQ, AWS SQS
  • โšก gRPC Support: High-performance RPC for microservices

Why Fennec?

Named after the Fennec fox ๐ŸฆŠ - the smallest, fastest, and most adaptable fox species native to the Sahara Desert and Tunisia. Just like the Fennec fox, our framework is:

  • ๐ŸฆŠ Small & Lightweight: Minimal dependencies, maximum efficiency
  • โšก Fast & Agile: Async/await throughout for blazing performance
  • ๐ŸŒ Adaptable: Works for any project size, from MVP to enterprise
  • ๐Ÿง  Smart: Elegant design with powerful features
  • ๐Ÿ‡น๐Ÿ‡ณ Tunisian Pride: Built with โค๏ธ in Tunisia

Installation

pip install fennec-framework uvicorn

Quick Start

Create Your First API

from fennec import Application, Router, JSONResponse

# Create application
app = Application(title="My API", version="1.0.0")
router = Router()

# Define routes
@router.get("/")
async def root():
    return JSONResponse(data={"message": "Hello World!"})

@router.get("/users/{id}")
async def get_user(id: int):
    return JSONResponse(data={"id": id, "name": "John Doe"})

# Include router
app.include_router(router)

# Run with: uvicorn main:app

With Validation

from fennec.validation import BaseModel, Field

class User(BaseModel):
    name: str = Field(min_length=2, max_length=50)
    email: str
    age: int
    
    def validate_age(self, value):
        if value < 0:
            raise ValueError("Age must be positive")
        return value

@router.post("/users")
async def create_user(request):
    data = await request.json()
    user = User(**data)  # Automatic validation
    return JSONResponse(data=user.dict(), status_code=201)

With Middleware

from fennec.security import CORSMiddleware, RateLimitMiddleware

# Add CORS
app.middleware_manager.add(CORSMiddleware(allow_origins=["*"]))

# Add rate limiting
app.middleware_manager.add(RateLimitMiddleware(max_requests=100, window_seconds=60))

# Custom middleware
@app.middleware("http")
async def log_requests(request, call_next):
    print(f"Request: {request.method} {request.path}")
    response = await call_next(request)
    return response

With Dependency Injection

from fennec.dependencies import Depends

def get_db():
    return {"users": []}

@router.get("/users")
async def list_users(db=Depends(get_db)):
    return JSONResponse(data={"users": db["users"]})

With Authentication

from fennec.security import JWTHandler, requires_auth

jwt = JWTHandler(secret_key="your-secret-key")

@router.post("/login")
async def login(request):
    data = await request.json()
    # Verify credentials...
    token = jwt.encode({"user_id": 1, "username": data["username"]})
    return JSONResponse(data={"token": token})

@router.get("/protected")
@requires_auth
async def protected_route(request):
    return JSONResponse(data={"message": "You are authenticated!"})

CLI Tools

Create New Project

python -m fennec.cli startproject myproject
cd myproject
python -m app.main

Create New Module

python -m fennec.cli create:module users

Run Development Server

python -m fennec.cli runserver --host=0.0.0.0 --port=8000

API Documentation

The framework automatically generates OpenAPI/Swagger documentation for your API.

Visit http://localhost:8000/docs to see interactive API documentation.

Testing

import asyncio
from fennec.testing import TestClient
from main import app

async def test_api():
    client = TestClient(app)
    
    # Test GET request
    response = await client.get("/users/1")
    assert response.status_code == 200
    assert response.json()["data"]["id"] == 1
    
    # Test POST request
    response = await client.post("/users", json_data={
        "name": "John",
        "email": "john@example.com",
        "age": 30
    })
    assert response.status_code == 201

asyncio.run(test_api())

Project Structure

myproject/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ main.py              # Application entry point
โ”‚   โ”œโ”€โ”€ routers/             # Route handlers
โ”‚   โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”‚   โ””โ”€โ”€ users.py
โ”‚   โ”œโ”€โ”€ models/              # Data models
โ”‚   โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”‚   โ””โ”€โ”€ user.py
โ”‚   โ”œโ”€โ”€ services/            # Business logic
โ”‚   โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”‚   โ””โ”€โ”€ user_service.py
โ”‚   โ””โ”€โ”€ db/                  # Database layer
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ””โ”€โ”€ repositories.py
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_users.py
โ””โ”€โ”€ README.md

Core Concepts

Application

The main application class that manages the lifecycle and ASGI interface.

app = Application(title="My API", version="1.0.0", docs_enabled=True)

Router

Manages routes and URL mapping.

router = Router(prefix="/api/v1")

@router.get("/users")
async def list_users():
    return JSONResponse(data={"users": []})

app.include_router(router)

Request & Response

@router.post("/data")
async def handle_data(request):
    # Get JSON body
    data = await request.json()
    
    # Get headers
    auth = request.headers.get("authorization")
    
    # Get path parameters
    user_id = request.path_params.get("id")
    
    # Get query parameters
    page = request.query_params.get("page", 1)
    
    return JSONResponse(
        data={"result": "success"},
        message="Data processed",
        status_code=200
    )

Validation

from fennec.validation import BaseModel, Field

class Product(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    price: float
    quantity: int = Field(default=0, required=False)
    
    def validate_price(self, value):
        if value < 0:
            raise ValueError("Price must be positive")
        return value

Middleware

from fennec.middleware import Middleware

class CustomMiddleware(Middleware):
    async def __call__(self, request, call_next):
        # Pre-processing
        request.custom_data = "something"
        
        # Call next middleware/handler
        response = await call_next(request)
        
        # Post-processing
        response.headers["X-Custom"] = "value"
        
        return response

app.middleware_manager.add(CustomMiddleware())

Exception Handling

from fennec.exceptions import HTTPException

class CustomException(HTTPException):
    def __init__(self):
        super().__init__(418, "I'm a teapot")

@app.exception_handler(CustomException)
async def handle_custom(request, exc):
    return JSONResponse(
        message=exc.message,
        status="error",
        status_code=exc.status_code
    )

WebSocket Support (v0.2.0)

Build real-time applications with WebSocket support:

from fennec import Application, WebSocketRouter, WebSocket, WebSocketManager

app = Application()
ws_router = WebSocketRouter()
ws_manager = WebSocketManager()

@ws_router.websocket("/ws/chat/{room}")
async def chat_room(websocket: WebSocket, room: str):
    await websocket.accept()
    client_id = await ws_manager.connect(websocket)
    ws_manager.join_room(client_id, room)
    
    try:
        while True:
            message = await websocket.receive_text()
            await ws_manager.broadcast(message, room=room)
    finally:
        await ws_manager.disconnect(client_id)

app.include_websocket_router(ws_router)

GraphQL Support (v0.2.0)

Create flexible GraphQL APIs:

from fennec import Application, GraphQLEngine, query, mutation

app = Application()
gql = GraphQLEngine()

# Define schema
schema = """
type User {
    id: ID!
    name: String!
    email: String!
}

type Query {
    users: [User!]!
    user(id: ID!): User
}

type Mutation {
    createUser(name: String!, email: String!): User!
}
"""

gql.set_schema(schema)

# Define resolvers
@query("users")
async def resolve_users(parent, info):
    return get_all_users()

@mutation("createUser")
async def resolve_create_user(parent, info, name: str, email: str):
    return create_user(name, email)

# Add GraphQL endpoint with GraphiQL interface
app.add_graphql("/graphql", gql, graphiql=True)

Enhanced Security (v0.2.0)

Production-ready security features:

from fennec.security import (
    PasswordHasher,
    SecurityHeadersMiddleware,
    CSRFMiddleware,
    InputSanitizer,
    EnhancedJWTHandler
)

# Password hashing with bcrypt
hashed = PasswordHasher.hash("password123")
is_valid = PasswordHasher.verify("password123", hashed)

# Security headers
app.middleware_manager.add(SecurityHeadersMiddleware())

# CSRF protection
app.middleware_manager.add(CSRFMiddleware(secret_key="your-secret"))

# Input sanitization
clean_html = InputSanitizer.sanitize_html(user_input)
email = InputSanitizer.validate_email(email_input)

# Enhanced JWT with refresh tokens
jwt = EnhancedJWTHandler(secret_key="your-secret")
access_token = jwt.create_access_token({"user_id": 1})
refresh_token = jwt.create_refresh_token({"user_id": 1})
new_token = jwt.refresh_access_token(refresh_token)

Redis Caching (v0.3.0)

Boost performance with Redis caching:

from fennec.cache import RedisCache, cache, CacheAside

# Initialize cache
redis_cache = RedisCache(url="redis://localhost:6379")

# Use decorator for automatic caching
@cache(ttl=300, backend=redis_cache)
async def get_expensive_data(user_id: int):
    # This result will be cached for 5 minutes
    return await database.query(user_id)

# Use cache-aside pattern
cache_aside = CacheAside(redis_cache, ttl=600)

async def get_user(user_id: int):
    return await cache_aside.get(
        f"user:{user_id}",
        lambda: database.get_user(user_id)
    )

Database Migrations (v0.3.0)

Manage database schema changes:

from fennec.migrations import MigrationManager

# Initialize migration manager
manager = MigrationManager(connection, migrations_dir="migrations")

# Create migration
await manager.create("add users table", migration_type="python")

# Apply migrations
await manager.migrate()

# Rollback
await manager.rollback(steps=1)

# Check status
status = await manager.status()

Monitoring & Observability (v0.3.0)

Production-ready monitoring:

from fennec.monitoring import PrometheusMetrics, RequestTracer, StructuredLogger

# Prometheus metrics
metrics = PrometheusMetrics(app_name="my_app")
app.add_middleware(MetricsMiddleware(metrics))

# Distributed tracing
tracer = RequestTracer(service_name="my_app")
app.add_middleware(TracingMiddleware(tracer))

# Structured logging
logger = StructuredLogger(name="my_app")
logger.info("User logged in", user_id=123, ip="192.168.1.1")

# Expose metrics endpoint
@app.get("/metrics")
async def metrics_endpoint(request):
    return Response(metrics.generate_metrics(), content_type=metrics.get_content_type())

Admin Dashboard (v0.3.0)

Web-based monitoring interface:

from fennec.admin import AdminDashboard

# Initialize admin dashboard
admin = AdminDashboard(
    app,
    auth_required=True,
    prefix="/admin"
)

# Access dashboard at http://localhost:8000/admin
# View real-time metrics, system health, and request logs

Message Queues (v0.3.0)

Async task processing:

from fennec.queue import QueueManager, Worker

# Initialize queue manager
queue = QueueManager(backend="redis", connection_url="redis://localhost:6379")

# Define tasks
@queue.task(queue_name="emails", max_retries=3)
async def send_email(to: str, subject: str, body: str):
    await email_service.send(to, subject, body)

# Enqueue task
await send_email("user@example.com", "Hello", "Welcome!")

# Start worker
worker = Worker(queue, queue_names=["emails"], concurrency=4)
await worker.start()

gRPC Support (v0.3.0)

High-performance microservices:

from fennec.grpc import GRPCServer, GRPCClient

# Server
server = GRPCServer(host="0.0.0.0", port=50051)
server.add_service(UserServiceStub, UserServicer())
await server.start()

# Client
client = GRPCClient(host="localhost", port=50051)
await client.connect()

response = await client.call(
    UserServiceStub,
    'GetUser',
    GetUserRequest(id=1)
)

Examples

Check the examples/ directory for complete working examples:

v0.1.0 Examples

  • simple_api: Basic CRUD API with validation and middleware

v0.2.0 Examples

  • websocket_chat: Real-time chat application with rooms
  • graphql_api: GraphQL API with queries and mutations
  • microservices: API Gateway with user and order services
  • database_integration: PostgreSQL and MongoDB examples
  • oauth2_auth: OAuth2 authorization code flow
  • deployment: Docker, docker-compose, and cloud deployment

v0.3.0 Examples

  • caching_example: Redis caching with decorators and strategies
  • migration_example: Database migration workflow
  • monitoring_setup: Prometheus, tracing, and logging
  • admin_dashboard: Real-time monitoring interface
  • message_queue: Async task processing with workers
  • grpc_service: gRPC microservice with Protocol Buffers

โš ๏ธ Security Notice

This framework is currently suitable for development and testing only.

Before deploying to production, please read:

Estimated time to production-ready: 1-2 days of security hardening.

Quick Links

Requirements

  • Python 3.8+
  • uvicorn (for running the server)

Development

# Install in development mode
pip install -e .

# Run quick test
python test_framework.py

# Run example (Method 1: with PYTHONPATH)
PYTHONPATH=$PWD python examples/simple_api/main.py

# Run example (Method 2: with script)
./run_example.sh

# Visit API documentation
# http://localhost:8001/docs

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License

About Fennec

The Name

Fennec (Vulpes zerda) is the smallest fox species, native to the Sahara Desert and North Africa, including Tunisia ๐Ÿ‡น๐Ÿ‡ณ. Known for their:

  • Large ears (excellent for hearing - like our great error messages!)
  • Small size (lightweight and efficient)
  • Speed and agility (async performance)
  • Adaptability (works in any environment)

The Mission

To provide developers with a simple, fast, and powerful framework that doesn't get in the way. We believe in:

  • Simplicity over complexity
  • Performance over features
  • Developer experience over everything

Made in Tunisia ๐Ÿ‡น๐Ÿ‡ณ

Proudly built in Tunisia, showcasing North African tech talent to the world.

Credits

Created by: Ghassen (Tunisia ๐Ÿ‡น๐Ÿ‡ณ) Inspired by: The Fennec fox, native to the Sahara Desert Built with: โค๏ธ, Python, and lots of coffee


Fennec Framework ๐ŸฆŠ
Small, Swift, and Adaptable

Branding Guide โ€ข Contributing โ€ข License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fennec_framework-0.3.0.tar.gz (135.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fennec_framework-0.3.0-py3-none-any.whl (85.9 kB view details)

Uploaded Python 3

File details

Details for the file fennec_framework-0.3.0.tar.gz.

File metadata

  • Download URL: fennec_framework-0.3.0.tar.gz
  • Upload date:
  • Size: 135.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for fennec_framework-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b70320d6e94ca163a3dd9ac6c57afe98cee49315e35ebea5e893fa06682e2b4c
MD5 90e5c9eac4f64251bc6d03b17e393ed0
BLAKE2b-256 df27052b409b5dbddcd15a7062254dd6ac83ea7aacaf798e777ed18ff7079af2

See more details on using hashes here.

File details

Details for the file fennec_framework-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fennec_framework-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 515aacd23a966e919e54b03134be95bb63dee70d44ceabbad01522a5bd7a4d29
MD5 0a329ad08bf98024b65c164f7ab0a3c6
BLAKE2b-256 410e1056f8c1f263e87e97b8cd03004afa146d9d5da2d4f1f595e87ef7331dac

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page