---
title: Version 23.12
---

# Version 23.12 (LTS)

.. toc::


## Introduction

This is the final release of the version 23 [release cycle](../../organization/policies.md#release-schedule). It is designated as a **long-term support ("LTS") release**, which means it will receive support for two years as stated in the support policy. If you run into any issues, please raise a concern on [GitHub](https://github.com/sanic-org/sanic/issues/new/choose.)

## What to know

More details in the [Changelog](../changelog.html). Notable new or breaking features, and what to upgrade:

### 🎉 Documentation now using {span:has-text-primary:Sanic}

![](http://127.0.0.1:8000/assets/images/sanic-framework-logo-circle-128x128.png)

You can read [all about how](/en/built-with-sanic.html), but we converted this documentation site to using the SHH 🤫 stack.

- [Sanic](https://sanic.dev)
- [html5tagger](https://github.com/sanic-org/html5tagger)
- [HTMX](https://htmx.org/)

### 👶 *BETA* Welcome to the Sanic interactive console

That's right, Sanic now ships with a REPL!

![](/assets/images/repl.png)

When using the Sanic CLI, you can pass the `--repl` argument to automatically run an interactive console along side your application. This is extremely helpful while developing, and allows you access to the application instance, as well as a built-in client enabled to send HTTP requests to the running instance.

If you use the `--dev` flag, this feature is quasi-enabled by default. While it will not outright run the REPL, it will start the process and allow you to enter the REPL at anytime by hitting `<ENTER>` on your keyboard.

*This is still in BETA mode. We would appreciate you letting us know about any any enhancement requests or issues.*

### Python 3.12 support

We have added Python 3.12 to the supported versions.

### Start and restart arbitrary processes

Using the [multiplexer](../../guide/running/manager.md#access-to-the-multiplexer), you can now start and restart arbitrary or pre-existing processes. This enabled the following new features in the way the multiplexer and worker manager operate:

1. `multiplexer.restart("<process name>")` will now restart a targeted single process
2. `multiplexer.manage(...)` is a new method that works exactly like `manager.manage(...)`
3. The `manage` methods now have additional keyword arguments:
   - `tracked` - whether the state of the process is tracked after the process completes
   - `restartable` - whether the process should be allowed to be restarted
   - `auto_start` - whether the process should be started immediately after it is created

```python
def task(n: int = 10, **kwargs):
    print("TASK STARTED", kwargs)
    for i in range(n):
        print(f"Running task - Step {i+1} of {n}")
        sleep(1)

@app.get("/restart")
async def restart_handler(request: Request):
    request.app.m.restart("Sanic-TEST-0")
    return json({"foo": request.app.m.name})


@app.get("/start")
async def start_handler(request: Request):
    request.app.m.manage("NEW", task, kwargs={"n": 7}, workers=2)
    return json({"foo": request.app.m.name})

@app.main_process_ready
def start_process(app: Sanic):
    app.manager.manage("TEST", task, kwargs={"n": 3}, restartable=True)
```

### Prioritized listeners and signals

In [v22.9](../2022/v22.9.md) Sanic added prioritization to middleware to allow arbitrary ordering of middleware. This same concept has now been extended to listeners and signals. This will allow a priority number to be assigned at creation time that will override its default position in the execution timeline.

```python
@app.before_server_start(priority=3)
async def sample(app):
    ...
```

The higher the number, the higher priority it will receive. Overall the rules for deciding the order of execution are as follows:

1. Priority in descending order
2. Application listeners before Blueprint listeners
3. Registration order

*Remember, some listeners are executed in reverse order*

### Websocket signals

We have added three new signals for websockets:

1. `websocket.handler.before`
2. `websocket.handler.after`
3. `websocket.handler.exception`

```python
@app.signal("websocket.handler.before")
async def ws_before(request: Request, websocket: Websocket):
    ...

@app.signal("websocket.handler.after")
async def ws_after(request: Request, websocket: Websocket):
    ...
    
@app.signal("websocket.handler.exception")
async def ws_exception(
    request: Request, websocket: Websocket, exception: Exception
):
    ...
```

![](https://camo.githubusercontent.com/ea2894c88bedf37a4f12f129569e8fd14bfceaa36d4452c7b7a1869d2f1cdb18/68747470733a2f2f7a692e66692f77732d7369676e616c732e706e67)

### Simplified signals

Sanic has always enforced a three part naming convention for signals: `one.two.three`. However, now you can create simpler names that are only a single part.

```python
@app.signal("foo")
async def foo():
    ...
```

You can make that part dynamic just like with regular signals and routes:

```python
@app.signal("<thing>")
async def handler(**kwargs):
    print("foobar signal received")
    print(kwargs)


@app.route("/")
async def test(request: Request):
    await request.app.dispatch("foobar")
    return json({"hello": "world"})
```

If you need to have multiple dynamic signals, then you should use the longer three-part format.

### The `event` method has been updated

A number of changes have been made to both `app.event()` and `blueprint.event()`.

- `condition` and `exclusive` are keywords to control matching conditions (similar to the `signal()` methods)
- You can pass either a `str` or an `Enum` (just like `signal()`)
- returns a copy of the context that was passed to the `dispatch()` method

### Reload trigger gets changed files

The files changed by the reloader are now injected into the listener. This will allow the trigger to do something with knowledge of what those changed files were.

```python
@app.after_reload_trigger
async def after_reload_trigger(_, changed):
    print(changed)
```

## Thank you

Thank you to everyone that participated in this release: :clap:

[@ahopkins](https://github.com/ahopkins)
[@ChihweiLHBird](https://github.com/ChihweiLHBird)
[@freddiewanah](https://github.com/freddiewanah)
[@gluhar2006](https://github.com/gluhar2006)
[@iAndriy](https://github.com/iAndriy)
[@MichaelHinrichs](https://github.com/MichaelHinrichs)
[@prryplatypus](https://github.com/prryplatypus)
[@SaidBySolo](https://github.com/SaidBySolo)
[@sjsadowski](https://github.com/sjsadowski)
[@talljosh](https://github.com/talljosh)
[@tjni](https://github.com/tjni)
[@Tronic](https://github.com/Tronic)

---

If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: [financial contributions](https://opencollective.com/sanic-org/).
