Skip to main content

PyElox: A Minimalist Web Framework PyElox is a fast, secure, and compact Python web framework built from the socket up, designed for performance, modularity, and explicit control.

🚀 Quick Installation To use PyElox, you need to install the core dependencies.

pip install python-dotenv Pillow

Pyelox is not yet on PyPI, but after publication:

pip install pyelox

⚙️ Project Setup and Configuration (.env) PyElox relies on a .env file in your project root for crucial settings, especially security.

Create a file named .env:

PYELOX_DEBUG=True
PYELOX_SECRET_KEY=A-STRONG-AND-UNIQUE-SECRET-KEY-FOR-SESSION-SECURITY
PYELOX_DB_NAME=data/pyelox.db

Debugging and Warnings When PYELOX_DEBUG=True:

Detailed Tracebacks are displayed in the browser in English when an uncaught exception occurs.

Security Warnings are logged to the console (e.g., if PYELOX_SECRET_KEY is set to its default unsafe value, or if access comes from a loopback IP).

💡 Usage Example (APP.py) This example demonstrates routing, templating, and initializing the database with a secure admin user.

from pyelox import PyElox
from pyelox.security import hash_password
from pyelox.sql import CREATE_USERS_TABLE, SELECT_ADMIN_USER, INSERT_USER

app = PyElox()

def _setup_db():
    try:
        app.db.execute_query(CREATE_USERS_TABLE)
    except Exception:
        pass

    # Check if admin exists using the secure fetch_query method
    if not app.db.fetch_query(SELECT_ADMIN_USER):
        admin_password_hash = hash_password('123')
        
        app.db.execute_query(
            INSERT_USER, 
            ['admin', admin_password_hash, 'Administrator']
        )

@app.route('/')
def index(request):
    return app.render('login.html', message="Please Log In")

@app.route('/dashboard')
def dashboard(request):
    username = request.get_query('user', 'Guest')
    return app.render('dashboard.html', username=username)

if __name__ == '__main__':
    _setup_db()
    app.run(host='127.0.0.1', port=8000, server_type='PyElox')

🔑 Core Architecture Details

  1. Database Management (pyelox/db.py) PyElox abstracts SQLite connections, handling concurrent access via threading locks, and ensures connection hygiene to prevent the Cannot operate on a closed database error.

Method

Purpose

Return Type

Usage

execute_query(sql, params)

Write Operations (CREATE, INSERT, UPDATE, DELETE). Commits changes.

sqlite3.Cursor (Do not call fetch methods on it).

app.db.execute_query(INSERT_USER, ['user', hash, 'Role'])

fetch_query(sql, params)

Raw Read Operations (SELECT). Executes query, fetches all results, and closes connection.

list of tuples (raw results).

data = app.db.fetch_query("SELECT id FROM users")

select(table, **filters)

High-Level Read (Recommended). Executes SELECT and converts results into a list of dict (key=column name).

list of dictionaries.

user = app.db.select('users', username='admin')

  1. SQL Statements Centralization (pyelox/sql.py) All raw SQL strings are stored in pyelox/sql.py as constants. This makes the application code cleaner and simplifies adapting to different database backends in the future.

  2. Security Utilities (pyelox/security.py) This module provides basic, essential security operations.

Function

Purpose

hash_password(password)

Securely hashes a plaintext password (currently using SHA-256).

verify_password(stored_hash, provided_password)

Safely compares a provided password against a stored hash using constant-time comparison.

  1. Routing and Requests Method

Purpose

@app.route(path)

Registers a view function for a specific URL path, supporting variable routing (/user/).

request.get_form(key)

Retrieves data from a submitted POST form.

request.get_query(key, default)

Retrieves data from URL query parameters (?key=value).

request.url_vars

Dictionary containing variables extracted from the route path (e.g., the id in /user/).

SPACEL Server

We recommend SpaceL Server over PyElox Server.

Usage:

from pyelox import PyElox
from pyelox.security import hash_password, verify_password
from pyelox.sql import CREATE_USERS_TABLE, SELECT_ADMIN_USER, INSERT_USER
from pyelox.session_manager import SessionManager

app = PyElox()

app.register_extension('session_manager', SessionManager(app.SECRET_KEY))

def _setup_db():
    try:
        app.db.execute_query(CREATE_USERS_TABLE)
    except Exception:
        pass

    if not app.db.fetch_query(SELECT_ADMIN_USER):
        admin_password_hash = hash_password('123')
        
        app.db.execute_query(
            INSERT_USER, 
            ['admin', admin_password_hash, 'Administrator']
        )

@app.route('/')
def index(request):
    return app.render('login.html', message="")

@app.route('/login')
def login_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        user_data = app.db.select('users', username=username)
        
        if user_data and verify_password(user_data[0]['password'], password):
            user = user_data[0]
            
            session_manager = app.get_tool('session_manager')
            session_id, session_cookie_value = session_manager.create_session(user['username'])
            
            response_html = f"""
                <html>
                <head><meta http-equiv="refresh" content="0;url=/dashboard?user={user['username']}"></head>
                <body>Redirecting...</body>
                </html>
            """
            
            headers = {
                'Set-Cookie': f'pyelox_session={session_cookie_value}; HttpOnly; Secure; Max-Age=3600; Path=/'
            }
            
            return (response_html.encode('utf-8'), 'text/html; charset=utf-8', headers)
        else:
            return app.render('login.html', message="Incorrect username or password.")

    return app.render('login.html', message="")

@app.route('/register')
def register_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        if not username or not password:
            return app.render('register.html', message="Username and Password are required.")
            
        existing_users = app.db.select('users', username=username)
        if existing_users:
            return app.render('register.html', message="User already exists.")

        try:
            hashed_password = hash_password(password)
            
            app.db.execute_query(
                INSERT_USER, 
                [username, hashed_password, 'User']
            )
            
            return app.render('login.html', message="Registration successful! Please log in.")
        except Exception:
            return app.render('register.html', message="Error saving user to DB.")

    return app.render('register.html', message="")

@app.route('/dashboard')
def dashboard(request):
    username = request.get_query('user', 'Guest')
    
    session_manager = app.get_tool('session_manager')
    
    session_status = "N/A"
    
    if 'Cookie' in request.headers:
        cookies = request.headers['Cookie']
        if 'pyelox_session=' in cookies:
            session_cookie = cookies.split('pyelox_session=')[1].split(';')[0]
            session_data = session_manager.get_session_data_from_cookie(session_cookie)
            
            if session_data:
                session_manager.update_session(session_data['id'], request.remote_addr)
                session_status = f"Session Active for: {session_data['user']} (ID: {session_data['id'][:8]})"
                
    return app.render('dashboard.html', 
                      username=username,
                      proposta="SpaceL is running securely with built-in Session Management. Check Admin GUI!",
                      status=session_status)

if __name__ == '__main__':
    _setup_db()
    app.run(host='127.0.0.1', port=8000, server_type='SpaceL')

Html Syntax:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>PyElox Dashboard</title>
    <link rel="stylesheet" href="/static/style.css">
</head>
<body>
    <header>
        <h1>PyElox Dashboard</h1>
        <nav><a href="/">Logout</a></nav>
    </header>
    
    <main>
        <h2>Welcome, {{ username }}!</h2>
        <p class="status-message">{{ status }}</p>
        
        <div class="test-proposta">
            <h3>Test Proposal: Persistence</h3>
            <p>{{ proposta }}</p>
            <p>If you shut down the server and run it again, your credentials will still be in the <code class="db-file">data/pyelox.db</code> file, and you will be able to log in again.</p>
        </div>
        
    </main>
    
    <footer>
        <p>Framework under pure Python development.</p>
    </footer>
</body>
</html>

Cors Python Script

from pyelox import PyElox
from pyelox.security import hash_password, verify_password
from pyelox.sql import CREATE_USERS_TABLE, SELECT_ADMIN_USER, INSERT_USER
from pyelox.session_manager import SessionManager
from pyelox.env import get_env
from dotenv import load_dotenv
import json 

load_dotenv(override=True)

app = PyElox()

app.register_extension('session_manager', SessionManager(app.SECRET_KEY))

def _setup_db():
    """Sets up the database and ensures the default admin user exists."""
    try:
        app.db.execute_query(CREATE_USERS_TABLE)
    except Exception:
        pass

    if not app.db.fetch_query(SELECT_ADMIN_USER):
        admin_password_hash = hash_password('123')
        
        app.db.execute_query(
            INSERT_USER, 
            ['admin', admin_password_hash, 'Administrator']
        )

@app.route('/')
def index(request):
    return app.render('login.html', message="")

@app.route('/login')
def login_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        user_data = app.db.select('users', username=username)
        
        if user_data and verify_password(user_data[0]['password'], password):
            user = user_data[0]
            
            session_manager = app.get_tool('session_manager')
            session_id, session_cookie_value = session_manager.create_session(user['username'])
            
            response_html = f"""
                <html>
                <head><meta http-equiv="refresh" content="0;url=/dashboard?user={user['username']}"></head>
                <body>Redirecting...</body>
                </html>
            """
            
            headers = {
                'Set-Cookie': f'pyelox_session={session_cookie_value}; HttpOnly; Secure; Max-Age=3600; Path=/'
            }
            
            return (response_html.encode('utf-8'), 'text/html; charset=utf-8', headers)
        else:
            return app.render('login.html', message="Incorrect username or password.")

    return app.render('login.html', message="")

@app.route('/register')
def register_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        if not username or not password:
            return app.render('register.html', message="Username and Password are required.")
            
        existing_users = app.db.select('users', username=username)
        if existing_users:
            return app.render('register.html', message="User already exists.")

        try:
            hashed_password = hash_password(password)
            
            app.db.execute_query(
                INSERT_USER, 
                [username, hashed_password, 'User']
            )
            
            return app.render('login.html', message="Registration successful! Please log in.")
        except Exception:
            return app.render('register.html', message="Error saving user to DB.")

    return app.render('register.html', message="")

@app.route('/dashboard')
def dashboard(request):
    username = request.get_query('user', 'Guest')
    
    session_manager = app.get_tool('session_manager')
    session_status = "N/A"
    
    # Check session
    if 'Cookie' in request.headers:
        cookies = request.headers['Cookie']
        if 'pyelox_session=' in cookies:
            session_cookie = cookies.split('pyelox_session=')[1].split(';')[0]
            session_data = session_manager.get_session_data_from_cookie(session_cookie)
            
            if session_data:
                session_manager.update_session(session_data['id'], request.remote_addr)
                session_status = f"Active Session for: {session_data['user']} (ID: {session_data['id'][:8]})"
                
    return app.render('dashboard.html', 
                      username=username,
                      proposta="SpaceL is running with integrated session management. Check the Admin GUI!",
                      status=session_status)

@app.route('/api/data')
def api_data(request):
    """Simple API endpoint to test CORS functionality."""
    
    # Respond to the browser's Preflight Request
    if request.method == 'OPTIONS':
        return ('', '', {})

    if request.method == 'POST':
        data = {'message': 'Data successfully received and processed via POST with CORS!', 'source': 'PyElox Server'}
    else:
        data = {'message': 'Data successfully retrieved via GET.', 'source': 'PyElox Server'}
    
    response_body = json.dumps(data).encode('utf-8')
    content_type = 'application/json; charset=utf-8'
    
    return (response_body, content_type, {})

@app.route('/cors-test')
def cors_test_page(request):
    """Loads the HTML page with the JavaScript client to perform the CORS test."""
    return app.render('cors_tester.html', target_url="http://127.0.0.1:8000/api/data")


if __name__ == '__main__':
    _setup_db()
    
    host = get_env('PYELOX_HOST', '127.0.0.1')
    
    # --- Safe port handling ---
    raw_port = get_env('PYELOX_PORT', '8000')
    port = 8000 
    
    try:
        if raw_port and raw_port.lower() != 'none':
            port = int(raw_port)
    except ValueError:
        print(f"Warning: Invalid value for PYELOX_PORT ('{raw_port}'). Using default port 8000.")
    # --------------------------
    
    app.run(host=host, port=port, server_type='PyElox')

Cors Html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CORS Test Page</title>
    <style>
        body {
            font-family: 'Inter', sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f7f7f9;
            color: #333;
            min-height: 100vh;
            display: flex;
            justify-content: center;
        }
        .container {
            max-width: 600px;
            width: 100%;
            background-color: #fff;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
        }
        h1 {
            color: #2c3e50;
            border-bottom: 2px solid #ecf0f1;
            padding-bottom: 10px;
        }
        .info-box {
            background-color: #ecf0f1;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            line-height: 1.6;
        }
        .info-box code {
            font-weight: bold;
            color: #e74c3c;
        }
        #testButton {
            background-color: #3498db;
            color: white;
            padding: 12px 20px;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s, transform 0.1s;
            width: 100%;
        }
        #testButton:hover {
            background-color: #2980b9;
        }
        #testButton:active {
            transform: scale(0.99);
        }
        #results {
            margin-top: 25px;
            padding: 15px;
            border-radius: 8px;
            min-height: 100px;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .success {
            background-color: #e6ffe6;
            border: 1px solid #00cc00;
            color: #008000;
        }
        .error {
            background-color: #ffe6e6;
            border: 1px solid #cc0000;
            color: #cc0000;
        }
        .loading {
            background-color: #f9f9e6;
            border: 1px solid #cccc00;
            color: #888800;
        }
        .code-block {
            background-color: #f4f4f4;
            padding: 10px;
            border-radius: 4px;
            margin-top: 15px;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>CORS Test Page</h1>
        <div class="info-box">
            <p>You are accessing this page on the PyElox server. The test below simulates a request that **forces** the browser to send a CORS verification request (the "Preflight Request," or **OPTIONS**).</p>
            <p>Test URL: <code>{{ target_url }}</code></p>
        </div>
        
        <button id="testButton">Execute POST Test (Forces CORS Preflight)</button>
        
        <h2>Results:</h2>
        <div id="results" class="loading">
            Awaiting test execution...
        </div>
        
        <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank" style="display: block; margin-top: 20px; color: #3498db;">What is CORS?</a>
    </div>

    <script>
        const targetUrl = "{{ target_url }}";
        const resultsDiv = document.getElementById('results');
        const testButton = document.getElementById('testButton');

        async function runCorsTest() {
            resultsDiv.className = 'loading';
            resultsDiv.innerHTML = 'Sending OPTIONS (Preflight) and POST requests...';
            testButton.disabled = true;

            try {
                const response = await fetch(targetUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ action: 'cors_check' })
                });

                if (response.ok) {
                    const data = await response.json();
                    resultsDiv.className = 'success';
                    resultsDiv.innerHTML = `
                        ✅ **SUCCESS!** The CORS request (POST) functioned correctly. Status: ${response.status}.<br>
                        The server successfully processed the Preflight (OPTIONS) request.<br>
                        Response Body (JSON):
                        <div class="code-block">${JSON.stringify(data, null, 2)}</div>
                    `;
                } else {
                    resultsDiv.className = 'error';
                    resultsDiv.innerHTML = `
                        ❌ **CORS/NETWORK ERROR**: The request returned an error status: ${response.status}.
                        This usually means the POST request was blocked or failed after the Preflight.
                        Detailed Status: ${response.statusText}
                    `;
                }
            } catch (error) {
                resultsDiv.className = 'error';
                resultsDiv.innerHTML = `
                    ❌ **CORS/NETWORK ERROR**: The request was blocked by the browser. This means the server failed to respond to the OPTIONS (Preflight) request with the correct CORS headers, or a network error occurred.
                    Detailed Error: ${error.message}
                `;
            } finally {
                testButton.disabled = false;
            }
        }

        testButton.addEventListener('click', runCorsTest);

        document.addEventListener('DOMContentLoaded', () => {
             resultsDiv.innerHTML = 'Click the button to start the test.';
             resultsDiv.className = '';
        });
    </script>
</body>
</html>

Cors SpaceL

from pyelox import PyElox
from pyelox.security import hash_password, verify_password
from pyelox.sql import CREATE_USERS_TABLE, SELECT_ADMIN_USER, INSERT_USER
from pyelox.session_manager import SessionManager
from pyelox.env import get_env
from dotenv import load_dotenv
import json 

load_dotenv(override=True)

app = PyElox()

app.register_extension('session_manager', SessionManager(app.SECRET_KEY))

def _setup_db():
    """Sets up the database and ensures the default admin user exists."""
    try:
        app.db.execute_query(CREATE_USERS_TABLE)
    except Exception:
        pass

    if not app.db.fetch_query(SELECT_ADMIN_USER):
        admin_password_hash = hash_password('123')
        
        app.db.execute_query(
            INSERT_USER, 
            ['admin', admin_password_hash, 'Administrator']
        )

@app.route('/')
def index(request):
    return app.render('login.html', message="")

@app.route('/login')
def login_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        user_data = app.db.select('users', username=username)
        
        if user_data and verify_password(user_data[0]['password'], password):
            user = user_data[0]
            
            session_manager = app.get_tool('session_manager')
            session_id, session_cookie_value = session_manager.create_session(user['username'])
            
            response_html = f"""
                <html>
                <head><meta http-equiv="refresh" content="0;url=/dashboard?user={user['username']}"></head>
                <body>Redirecting...</body>
                </html>
            """
            
            headers = {
                'Set-Cookie': f'pyelox_session={session_cookie_value}; HttpOnly; Secure; Max-Age=3600; Path=/'
            }
            
            return (response_html.encode('utf-8'), 'text/html; charset=utf-8', headers)
        else:
            return app.render('login.html', message="Incorrect username or password.")

    return app.render('login.html', message="")

@app.route('/register')
def register_handler(request):
    if request.method == 'POST':
        username = request.get_form('username')
        password = request.get_form('password')
        
        if not username or not password:
            return app.render('register.html', message="Username and Password are required.")
            
        existing_users = app.db.select('users', username=username)
        if existing_users:
            return app.render('register.html', message="User already exists.")

        try:
            hashed_password = hash_password(password)
            
            app.db.execute_query(
                INSERT_USER, 
                [username, hashed_password, 'User']
            )
            
            return app.render('login.html', message="Registration successful! Please log in.")
        except Exception:
            return app.render('register.html', message="Error saving user to DB.")

    return app.render('register.html', message="")

@app.route('/dashboard')
def dashboard(request):
    username = request.get_query('user', 'Guest')
    
    session_manager = app.get_tool('session_manager')
    session_status = "N/A"
    
    # Check session
    if 'Cookie' in request.headers:
        cookies = request.headers['Cookie']
        if 'pyelox_session=' in cookies:
            session_cookie = cookies.split('pyelox_session=')[1].split(';')[0]
            session_data = session_manager.get_session_data_from_cookie(session_cookie)
            
            if session_data:
                session_manager.update_session(session_data['id'], request.remote_addr)
                session_status = f"Active Session for: {session_data['user']} (ID: {session_data['id'][:8]})"
                
    return app.render('dashboard.html', 
                      username=username,
                      proposta="SpaceL is running with integrated session management. Check the Admin GUI!",
                      status=session_status)

@app.route('/api/data')
def api_data(request):
    """Simple API endpoint to test CORS functionality."""
    
    # Respond to the browser's Preflight Request
    if request.method == 'OPTIONS':
        return ('', '', {})

    if request.method == 'POST':
        data = {'message': 'Data successfully received and processed via POST with CORS!', 'source': 'PyElox Server'}
    else:
        data = {'message': 'Data successfully retrieved via GET.', 'source': 'PyElox Server'}
    
    response_body = json.dumps(data).encode('utf-8')
    content_type = 'application/json; charset=utf-8'
    
    return (response_body, content_type, {})

@app.route('/cors-test')
def cors_test_page(request):
    """Loads the HTML page with the JavaScript client to perform the CORS test."""
    return app.render('cors_tester.html', target_url="http://127.0.0.1:8000/api/data")


if __name__ == '__main__':
    _setup_db()
    
    host = get_env('PYELOX_HOST', '127.0.0.1')
    
    # --- Safe port handling ---
    raw_port = get_env('PYELOX_PORT', '8000')
    port = 8000 
    
    try:
        if raw_port and raw_port.lower() != 'none':
            port = int(raw_port)
    except ValueError:
        print(f"Warning: Invalid value for PYELOX_PORT ('{raw_port}'). Using default port 8000.")
    # --------------------------
    
    # Changed server_type to 'SpaceL' to use the SpaceLServer class
    app.run(host=host, port=port, server_type='SpaceL')

Metadata

Release files for pyelox 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyelox 0.1.1
File Size Uploaded Provenance Actions
pyelox-0.1.1.tar.gz 27.0 kB Without provenance Details for pyelox-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyelox 0.1.1
File Interpreter ABI Platform Actions
pyelox-0.1.1-py3-none-any.whl Python 3 none any Details for pyelox-0.1.1-py3-none-any.whl

Total release size: 50.9 kB

Release files / pyelox-0.1.1.tar.gz

Download URL pyelox-0.1.1.tar.gz
Size 27.0 kB
Tags Source
SHA-256 checksum
How to use checksums
043ae6ea821defddb13c71854e263c24f79dd097a1630a4b8ee9cb8756fc3c16
BLAKE2b-256 checksum
How to use checksums
f267f6955100b50213774fd74faa15d05e62176a9b94dcffc965357ccab8b6a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Release files / pyelox-0.1.1-py3-none-any.whl

Download URL pyelox-0.1.1-py3-none-any.whl
Size 23.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7f84dc0dd00be3df9d48c7cd75a94368de3a65bd5b3b27452bb9d5fb3b5a329b
BLAKE2b-256 checksum
How to use checksums
02c892987ac7aa21189e4332bd92e8b403b903cec7553ac44d6d2249391d2ad1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Security for pyelox 0.1.1

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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page