Run async functions seamlessly from sync code using a persistent background event loop.
Project description
palitra
a.k.a. "palette" — captures the essence of the library: blending differently colored (sync/async) functions like on an artist’s palette.
It's a lightweight bridge between synchronous and asynchronous Python code, maintaining a persistent event loop in a background thread. It allows you to call async def functions directly from regular (sync) code without blocking or complex event loop reentry.
Unlike asyncio.run(), which creates and tears down a new event loop on each call, using palitra.run() eliminates that overhead — preserving async state and resources (like aiohttp sessions or database connections) across multiple calls.
⚠️ Known issues: Unexpected behaviour at cleanup in free-threaded builds.
If something breaks in your environment, please report an issue—the whole purpose of this library is to spare developers from reinventing async/sync bridges in every project. Your feedback directly helps improve its reliability and real-world compatibility.
Inspired by Running async code from sync in Python asyncio by lemon24 and related discussions such as Celery #9058.
Installation
You can install palitra from PyPI with pip or any other Python package manager (uv, poetry, etc):
pip install palitra
Features
- ✅ Runs a persistent asyncio event loop in a background thread
- ✅ Simple, thread-safe API for running coroutines from sync code
- ✅ No monkey patching or global loop overrides
- ✅ Automatic cleanup via
atexitand weakref to global runner (if used) - ✅ Lightweight: no external dependencies
Documentation
Usage Examples
This is not ideal, but in real-world scenarios, migrating to native non-blocking/asyncio code isn’t always possible. When stuck with sync environment, palitra lets you still use async features to get things working.
Flask with aiohttp
from flask import Flask, jsonify
import palitra
import aiohttp # better use niquests btw
import asyncio
app = Flask(__name__)
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.json()
@app.route('/api/comments')
def get_comments():
async def fetch_all():
async with aiohttp.ClientSession() as session:
urls = [
'https://jsonplaceholder.typicode.com/comments/1',
'https://jsonplaceholder.typicode.com/comments/2',
'https://jsonplaceholder.typicode.com/comments/3',
]
return await asyncio.gather(*[fetch_url(session, url) for url in urls])
comments = palitra.run(fetch_all())
return jsonify(comments)
if __name__ == '__main__':
app.run()
Celery
import palitra
from celery import Celery
import asyncio
import time
celery_app = Celery('tasks', broker='pyamqp://guest@localhost//') # better migrate to taskiq
async def async_processing(data: str) -> dict:
await asyncio.sleep(0.5) # simulated async I/O
return {"input": data, "processed": True, "timestamp": time.time()}
@celery_app.task(name="process_async")
def sync_celery_wrapper(data: str):
return palitra.run(async_processing(data))
Contributing
Pull requests are welcome! Please:
- Document known issues or caveats
- Include test coverage for new features
- Keep the code as simple and minimal as possible
- Prefer clarity over cleverness
Things that need more work:
- Proper stress testing
- Verifying thread safety in edge cases
- Ensuring reliable shutdown under all conditions
- Debug free-threading
License
BSD-3-Clause
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file palitra-0.1.0.tar.gz.
File metadata
- Download URL: palitra-0.1.0.tar.gz
- Upload date:
- Size: 7.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab54c4e68033d6bd8d379b56c58b170aa3b8e0e5007565286135670226e620b1
|
|
| MD5 |
c9f5a7c7e319f7f7fcf24b8413e02b8c
|
|
| BLAKE2b-256 |
be8b0b67f982adf6d14e990fce69e90fada05a8a0b54d3ea59483a42c4f1d510
|
Provenance
The following attestation bundles were made for palitra-0.1.0.tar.gz:
Publisher:
ci.yml on albertedwardson/palitra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
palitra-0.1.0.tar.gz -
Subject digest:
ab54c4e68033d6bd8d379b56c58b170aa3b8e0e5007565286135670226e620b1 - Sigstore transparency entry: 976819789
- Sigstore integration time:
-
Permalink:
albertedwardson/palitra@6a19a02b31df328d172155ca0b08ba0f8314f1ea -
Branch / Tag:
refs/heads/release - Owner: https://github.com/albertedwardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@6a19a02b31df328d172155ca0b08ba0f8314f1ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file palitra-0.1.0-py3-none-any.whl.
File metadata
- Download URL: palitra-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffd45a57e1147a291ee2bdff7c3dac281875c5225c9d854925c637c4c9a237f3
|
|
| MD5 |
64abad131fb6f8619877c5124cc73312
|
|
| BLAKE2b-256 |
bac2dd45b25d02d4410bb44a9f5ef79babf0b3e56712ad9fd7ec5ec48c92ecc5
|
Provenance
The following attestation bundles were made for palitra-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on albertedwardson/palitra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
palitra-0.1.0-py3-none-any.whl -
Subject digest:
ffd45a57e1147a291ee2bdff7c3dac281875c5225c9d854925c637c4c9a237f3 - Sigstore transparency entry: 976819801
- Sigstore integration time:
-
Permalink:
albertedwardson/palitra@6a19a02b31df328d172155ca0b08ba0f8314f1ea -
Branch / Tag:
refs/heads/release - Owner: https://github.com/albertedwardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@6a19a02b31df328d172155ca0b08ba0f8314f1ea -
Trigger Event:
push
-
Statement type: