Skip to main content

A Simple fast lightweight webframework lib for python

Project description

Slime – A Rust + Python Hybrid Web Framework

Slime is a high-performance web framework that combines Rust and python

It is designed for developers who want Python developer experience with a Rust-powered server core. Slime is ideal for building:

  • Api
  • Real-time application
  • Web backends
  • Microservices
  • Streaming Services

Installation

    pip install slimeweb
    slime new ProjectName
    cd ProjectName
    slime run ProjectName

After running these commands, open your browser and navigate to localhost:3000. You'll see this message displayed:

Hello World from slime

Features

  • Python handler functions
  • Rust powered HTTP server
  • Multiple worker pool model
  • Sync & Async handler
  • Multipart form support
  • File uploads
  • Streaming Response
  • Cookie signing
  • Custom headers
  • JSON / HTML / raw responses
  • Templates rendering with context
  • Static serving
  • Hot reload of templates in dev mode
  • WebSocket
  • App state
  • Compression
  • Middleware Plugin

Project Structure

Project are created and init by uv after using below command, by default it runs in python no-gil mode, for max performance.

slime new ProjectName
root/
│
├── .venv/                 # Virtual environment
├── static/                # Static files (CSS, JS, images)
├── templates/             # HTML templates
│
├── .gitignore             # Git ignore rules
├── .python-version        # Python version specification
├── main.py                # Main application entry point
├── pyproject.toml         # Project configuration and dependencies
├── README.md              # Project documentation
└── uv.lock                # Locked dependency versions

Getting Started

Use the below code to create simple GET request in dev environment

from slimeweb import Slime

app = Slime(__file__)

@app.route(path="/", method="GET")
def home(req, resp):
    return resp.plain("Hello World from slime")

if __name__ == "__main__":
    app.serve(dev=True)

Slime Cli

    slime new projectName   -> Create new project
    slime run main          -> Run slime without GIL
    slime rung main         -> Run slime with GIL
    slime add packageName   -> Add lib to the project deps
    slime use python3.12    -> Change the python runtime

Basic Application

To create a route in slimeweb, Use route() method. Route method contains

  • path ('/' as default)
  • method ('GET' as default)
  • stream (content-type)
  • ws (create websocket for this path)
  • compression (SlimeCompression.NoCompression as default)

NOTE: You can define only one handler per unique route-method combination, defining multiple handlers for the same path and method will raise an error.

@app.route(path="/", method=["GET","POST"])
def index(req, resp):
    if req.method == "GET":
        return resp.plain("Hello from Slime")
    else:
        return resp.json({
            "status": "ok",
            "message": "Hello from Slime"
        })

Handlers in slimeweb can be written as either regular synchronous functions or asynchronous ones. Async handlers run using Python's asyncio event loop for efficient, non-blocking execution.

Every handler receives exactly two arguments:

  • SlimeRequest: The incoming request object, containing details like headers, body, and query parameters.

  • SlimeResponse: The response object you'll use to build and send the output back to the client.

The exact way you handle and populate the response depends on the route type (e.g., HTTP,Streaming or Websocket). Check the API & Examples reference below for type-specific details.

To start the Slime server we should use server() method.

 app.serve()

serve() has few optional argument you can pass

  • host (default 127.0.0.1)
  • port (default 3000)
  • secret_key (default None, used for cookie sign)
  • dev (default False)
  • app_state (default {})

Worker:

You can control the number of workers using the SLIME_WORKER environment variable.

  • In development mode, it defaults to 1 worker
  • In production, it automatically uses the number of CPU cores
 export SLIME_WORKER=3
 
 OR
 
 $ENV:SLIME_WORKER="3"

Request Body

Slime supports all kinds of request body

@app.route(path="/test", method="POST")
def hello(req, resp):
    print("query", req.query)
    print("params", req.params)
    print("body", req.body)
    print("json", req.json)
    print("form", req.form)
    print("text", req.text)
    print("bytes", req.bytes)
    print("file",req.file)
    return resp.json({"status": "ok"})

File Upload

In your Slime file handler, access uploaded files via the req.file attribute, it returns a list of SlimeFile objects (Refer below for SlimeFile api).

Note: For security, Slime automatically strips the original file extension and assigns a unique filename to each uploaded file.

@app.route(path="/test", method="POST")
def hello(req, resp):
    file = req.file[0] # use can upload multiple files
    print(file.filename)
    print(file.content_type)
    print(file.file_path)
    print(file.file_size)
    print(file.extension)
    file.save(f"testing_file.{file.extension}")
    return resp.json({"status": "ok"})

Compression

Slime supports response body compression to reduce payload size and improve performance.

from slimeweb import SlimeCompression
@app.route(path="/",method="GET",compression=SlimeCompression.Gzip)
def land(req,resp):
    resp.plain("hello" * 5000)

In this example, Gzip compression is enabled for the route. If the client requested for compression, Slime will automatically compress the response body before sending it. Refer Api for types of compression available.

NOTE: Compression body has a threshold slime will compress the body if the content size is above the threshold, to prevent unnecessary CPU cycle.

Template Render

@app.route(path="/", method="GET")
def land(req, resp):
    html = req.render("hello.html", **{"name": "abilash", "slimeVersion": "0.0.1"})
    return resp.html(html)

Few examples on rendering

<h1>Hello {{ name }}!</h1>

above will result like

<h1>Hello abilash!</h1>

Logic

{% if user %}
  Hello {{ user }}
{% else %}
  Hello Guest
{% endif %}

Loops

<ul>
{% for item in items %}
  <li>{{ item }}</li>
{% endfor %}
</ul>

You can also generate

  • Markdown
  • SQL
  • Custom Code
  • YAML
  • JSON
  • Config File
  • HTML & etc..

App State

App state allows you to maintain shared data across requests during your app's lifecycle.

Initialize app state when starting your server

app.serve(app_state={"counter": 0})

Within each handler, the current app state is automatically injected into the SlimeRequest object. Use these methods to interact with it:

  • req.get_state(key), To retrieve the current value for a given key.
  • req.update_state(key,value), To update the value for a given key.

NOTE: SlimeState is not atomic. In concurrent scenario with multiple simultaneous request, race condition may occur during state updates, Potentially leading to incorrect values. For production when working with high concurrency, consider implementing your own synchronization or use external state store.

Middleware

Middleware should be declared after declaring route handler

LifeCycle of request handler

Middle before request -> Router handler -> Middle after request

@app.middle_before(path="/", method="GET")
def land_before(req, resp):
    resp.set_header("AFTER", "REQUEST")


@app.middle_after(path="/", method="GET")
def land_after(req, resp):
    resp.set_header("BEFORE", "Request")

NOTE: Middleware handlers must match the route handler's type. If your route handler is asynchronous, the middleware must also be async (and vice versa for sync).

Middleware Plugin

Slime lets you create custom middleware plugins with the use() method. Define a plugin class with middle_after and middle_before methods. Both the mesthod should accept two argument SlimeRequest and SlimeResponse.

class SimpleMiddle:
    def middle_after(self, req, resp):
        resp.set_header("PluginAfter","CustomPlugin")
    def middle_before(self, req, resp):
        resp.set_header("PluginBefore","CustomPlugin")
        
        
if __name__ == "__main__":
    app.use(SimpleMiddle)
    # or 
    app.use(SimpleMiddle,method=["POST","GET"],path="/home")

This example builds a custom middleware plugin named SimpleMiddle with both middle_after and middle_before methods. To apply the plugin, we are using use() method, which targets all routes and HTTP methods by default. We can limit the scope of the plugin by specifying the route and the path.

NOTE: Plugin use() should be used after declaring the routes, otherwise error will be raised.

Streaming

Streaming in slime is simple and straightforward. When declaring your route, specify the stream's content-type. You can add any headers to the response before calling start_stream(). Once start_stream is called, streaming begins to the user. Use send() to stream data chunks and slime automatically serializes them before sending. Call close() when done to end the connection.

NOTE: Updating headers after start_stream() will cause an error.

@app.route(path="/stream", method="GET", stream="text/plain")
def stream_me(req, resp):
    resp.start_stream()
    for i in range(5):
        resp.send(i)
    resp.close()
    
    # OR u can use @app.stream

@app.stream(path="/stream", method="GET", content="text/plain")
def stream_me(req, resp):
    resp.start_stream()
    for i in range(5):
        resp.send(i)
    resp.close()    

WebSocket

WebSockets in Slime are event-driven, meaning Slime calls specific callback methods when key events happen.

You'll typically need two optional callbacks (not required):

  • One for when data is received from the client
  • One for when the client disconnects

In the echo example below, the read_me() callback first checks if the client is still connected. If yes, it echoes back the exact message it received.

NOTE: The on_message() callback must accept 1 argument which is the data sent by the client.

@app.websocket(path="/chat", method="GET")
def chatty(req, resp):

    def read_me(msg):
        if not resp.is_closed():
            resp.send(msg)    

    def close_me():
        pass

    resp.on_message(read_me)
    resp.on_close(close_me)

Api

Slime Request

  req.method -> str
  req.path -> str
  req.client -> str # client address
  req.header -> Dict[str,str]
  req.body -> Bytes
  req.bytes -> [Bytes] (u8)
  req.form -> Dict[str,str]
  req.file -> [SlimeFile]
  req.json -> Any
  req.query -> Dict[str,str]
  req.params -> Dict[str,str]
  req.text -> str
  req.secret_key -> str
  req.get_cookies() -> Dict[str,str]
  req.get_signed_cookie(key: str) -> str|None
  req.render(template_name: str,Dict[str,any]|None)
  req.no_of_files_available() -> int
  req.get_state(key: str) -> Any
  req.update_state(key: str,value: Any)

HTTP Slime Response

  resp.set_cookie(key: str,value: str) -> None
  resp.set_sign_cookie(key:str,value: str,secret: str) -> None
  resp.set_header(key: str,value: str) -> None
  resp.set_status(status_id: int) -> None
  # Here status is optional parameter 
  resp.plain(data: str,status=200)
  resp.json(data: any,status=200) # any Pyobject which we can serialize
  resp.html(data: str,status=200)
  

Stream Slime Response

  resp.content_type ->  str
  resp.headers -> Dict[str,str]
  resp.set_header(key: str,value: str)
  resp.start_stream() # to start the stream
  resp.send(data: any) # any Pyobject which we can serialize
  
  #send has optional parameter strict_order which is default as True 
  #if your streaming doesnt need to be in order it will create task for each send and run in async internally.
  # NOTE: If strict_order=False and send() is failed it will return silently with error in terminal.
  
  resp.close() # close stream
  

Websocket Slime Response

   resp.id -> str
   resp.on_message(handler: Callable) -> None
   resp.on_close(handler: Callable) -> None
   resp.send(data: any) # any Pyobject that we can serialize
   resp.is_closed() -> bool
  

SlimeFile

    slimefile_obj.filename -> str
    slimefile_obj.content_type -> str
    slimefile_obj.file_path -> str
    slimefile_obj.file_size -> int
    slimefile_obj.extension -> str
    slimefile_obj.save(new_filename: str) -> None
    slimefile_obj.clean() -> None # remove temp file

SlimeCompression

from slimeweb import SlimeCompression #Enum

    SlimeCompression.NoCompression  (default)
    SlimeCompression.Gzip
    SlimeCompression.Brotli
    SlimeCompression.Zstd

Benchmark

BenchMark Code with no-gil example:

Slimeweb benchmark with no-gil

License

This project is licensed under the terms of MIT license

Thank you & enjoy using SlimeWeb ❤️ ~ Abilash Suresh

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

slimeweb-0.1.5.tar.gz (5.4 MB view details)

Uploaded Source

Built Distribution

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

slimeweb-0.1.5-py3-none-any.whl (5.4 MB view details)

Uploaded Python 3

File details

Details for the file slimeweb-0.1.5.tar.gz.

File metadata

  • Download URL: slimeweb-0.1.5.tar.gz
  • Upload date:
  • Size: 5.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for slimeweb-0.1.5.tar.gz
Algorithm Hash digest
SHA256 29126070fe110774f89e8c3fae86dfa30ba02bb8fa42ff18c96cacba065dae90
MD5 0ebe755458448a94fe43cc416f2346d0
BLAKE2b-256 42508f749e773e6f67e8bd5d805342466cb142948dc61b7ba9ced8f8afbba6e7

See more details on using hashes here.

File details

Details for the file slimeweb-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: slimeweb-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 5.4 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for slimeweb-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 abc81e33003c1e3b3102a1eaee219099c2506072e0366ab6595047751e93cf9d
MD5 09c0292d7396518b0c09bdd67fdba410
BLAKE2b-256 f80dc063ce8d066261ad62c8f18fe4b29bd1ae1ef9f6cf3f538cade36883fd93

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