Skip to main content

🪟 orcsome3

Python 3 scripting for a NETWM-compliant X11 window manager you already run (Openbox, etc.). It is not a window manager.

Based in orcsome. Linux / X11 only. Requires Python 3.8 or newer (3.8, 3.9, 3.10, … current).

You write ~/.config/orcsome3/config.py. orcsome3 grabs keys, watches window create/destroy/property/focus, can change EWMH state and icons, and can post desktop notifications over D-Bus.

🏗️ Architecture

config.py  (your script)
  └─ orcsome3          WindowManager, Window, Notification, get_wm, keys
       └─ orcsome3.libs.xlib / ev     typed Python wrappers
            └─ orcsome3_backend.so    Cython (Xlib + statically linked ev / cairo / Magick / gd / resvg)
                 └─ libX11, libXext, libXss, libXtst   (system, dynamic)
  • Public API lives in orcsome3/ (get_wm, window_manager, notify, keys). That is what config.py should import.
  • Only orcsome3.libs.xlib and orcsome3.libs.ev import orcsome3_backend. Do not import the .so from config.py.
  • The backend is typed via a generated orcsome3_backend.pyi (make stubs). The package ships py.typed.
  • libev, Cairo, ImageMagick 7, gd, resvg (and their image-format deps) are downloaded and statically linked into the .so. You do not install ImageMagick from source yourself. X11 stays a normal system library.

✨ What you can do

  • Global and per-window hotkeys (XGrabKey), including CapsLock/NumLock variants
  • React to window create / manage / destroy / property / focus
  • NETWM/EWMH: desktops, maximize, fullscreen, decorations, close, move/resize, icons — set_icon takes SVG (via resvg) or PNG/JPEG/GIF/BMP/ICO/WebP/TIFF/… (via ImageMagick 7), not just one format
  • Freedesktop notifications (dbus-next → org.freedesktop.Notifications)
  • Typed config.py (mypy / basedpyright)

📦 Install

libev, Cairo, ImageMagick 7, gd, and resvg are all statically linked into orcsome3_backend.so. Whether you install the wheel from PyPI or build from source, you never install any of them yourself — the only external runtime dependency is X11.

X11 libraries

orcsome3 needs the usual X11 client libraries (libX11, libXext, libXss, libXtst). A Linux desktop almost always has them already. Install them only if the module fails to load:

# Debian / Ubuntu
sudo apt install libx11-6 libxss1 libxext6 libxtst6

# Arch
sudo pacman -S libx11 libxss libxext libxtst

Python deps (dbus-next, typing_extensions) come with the package.

From PyPI

python3 -m pip install orcsome3

Linux manylinux wheels cover CPython 3.8 through current (3.15 as of cibuildwheel 4.2). No musllinux, free-threaded (3.14t, …), Windows, or macOS. A GitHub Release (or Upload Python Package from Actions) publishes the sdist and wheels. Bump the pypa/cibuildwheel pins in .github/workflows/python-publish.yml when a new CPython ships.

Build from source

First-time native build fetches git/tarball sources and compiles static libs. You need a C/C++ toolchain, the X11 headers, and:

# Debian / Ubuntu
sudo apt install git cmake autoconf automake libtool pkg-config meson ninja-build \
  libx11-dev libxss-dev libxext-dev libxtst-dev curl
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y   # resvg

# Arch
sudo pacman -S git cmake autoconf automake libtool pkgconf meson ninja \
  libx11 libxss libxext libxtst rust

Then:

git clone https://github.com/ahsand97/orcsome3.git
cd orcsome3
make native          # static libs + orcsome3_backend.so  (slow once)
python3 -m pip install .

pip install . reuses orcsome3_built_libraries/; it does not download those libs again. Run make native first on a clean clone.

To hack without installing the package:

make dev             # venv + deps + native
make run             # python -m orcsome3  (needs ~/.config/orcsome3/config.py)

🚀 Quick start

Create ~/.config/orcsome3/config.py (or pass -c). If the file is missing, orcsome3 logs an error and exits.

Every hook except on_init/on_deinit/on_timer calls back as (window, event) — on_key/on_button/on_create and friends all follow this shape, so there's one pattern to learn. Most take window=None (every window) or window= a specific id (typically the window parameter from on_manage, nested). wm.event_window still works as a side-channel (e.g. from code that isn't itself inside a callback), but the parameter is what you'll normally use. Decorated functions get .remove() to unregister.

Decorators

  • @wm.on_init / @wm.on_deinit — no parentheses, no arguments. on_init runs once at startup after root events are selected, before existing clients are scanned. on_deinit runs in stop() after key/button grabs and timers are torn down.
  • @wm.on_key(...) — XGrabKey on KeyPress. Default is a global hotkey (grab on the root). The callback's window is that grab window; use wm.current_window for the focused client. Pass window_matcher= to grab on matching clients instead. orcsome3 consumes the key unless propagate_event=True. CapsLock/NumLock variants are grabbed for you. "Control + b" or a KeyDefinition; modifiers Control/Ctrl, Alt/Meta, Shift, Win/Super, AnyModifier; keys are X keysym names (XK_ stripped).
  • @wm.on_key_release(...) — same grab as on_key, but KeyRelease. Register both for the same combo; they share one XGrabKey.
  • @wm.on_button(...) — XGrabButton on ButtonPress (BUTTONS.Button1…Button5 or AnyButton). Same root-vs-window_matcher and propagate_event rules as on_key.
  • @wm.on_create(...) — CreateNotify, including windows already mapped when orcsome3 starts. Optional matcher=WindowMatchers(...). event is None for that startup sweep (no real CreateNotify exists for windows that already existed) and a real XCreateWindowEvent otherwise.
  • @wm.on_manage(...) — same as on_create, but skips the startup sweep, so event is always a real XCreateWindowEvent, never None. Nest per-window @wm.on_destroy(window=...) (and similar) here so you do not attach once per existing client.
  • @wm.on_destroy(...) — DestroyNotify. window is only that id; reading properties on it will fail (it is already gone).
  • @wm.on_property_change(property="...") — PropertyNotify when an atom (_NET_WM_STATE, …) gets a new value.
  • @wm.on_focus() / @wm.on_unfocus() — FocusIn / FocusOut (NotifyNormal / NotifyWhileGrabbed only; pointer-detail and grab-notify are ignored).
  • @wm.on_map() / @wm.on_unmap() / @wm.on_configure() — MapNotify / UnmapNotify / ConfigureNotify. Configure fires often during resize; size is event.width / event.height (not a later get_geometry()).
  • @wm.on_client_message(message_type="...") — ClientMessage filtered by atom name (_NET_ACTIVE_WINDOW, …).
  • @wm.on_timer(timeout=...) — repeating libev timer (seconds), callback takes no arguments. Return True to stop; None/falsy keeps it. Also .start() / .stop() / .again(). Needs the process event loop (normal orcsome3 has one).

Example config.py:

from pathlib import Path
from typing import Optional

from orcsome3 import get_wm
from orcsome3.keys import KeyboardModifiers, KeyDefinition, WindowMatchers
from orcsome3.libs.xlib import (
    BUTTONS,
    XButtonEvent,
    XClientMessageEvent,
    XConfigureEvent,
    XCreateWindowEvent,
    XDestroyWindowEvent,
    XFocusChangeEvent,
    XKeyEvent,
    XMapEvent,
    XPropertyEvent,
    XUnmapEvent,
)
from orcsome3.notify import Notification
from orcsome3.window_manager import Window, WindowManager

wm: WindowManager = get_wm()


# Hide the title bar while a window is maximized, restore it otherwise.
@wm.on_property_change(property="_NET_WM_STATE")
def hide_title_bar_when_maximized(window: Window, event: XPropertyEvent) -> None:
    if window.maximized_horz and window.maximized_vert:
        if window.decorated:
            window.set_state(decorate=False)
    elif not window.decorated:
        window.set_state(decorate=True)


@wm.on_init
def on_start() -> None:
    print("orcsome3 started")


@wm.on_deinit
def on_stop() -> None:
    print("orcsome3 stopping")


# Global hotkey. `window` is root; focused client is `wm.current_window`.
@wm.on_key(key_definition="Control + b")
def on_control_b(window: Window, event: XKeyEvent) -> None:
    print("Control + b")


# Per-client grab (every URxvt, including ones already mapped after init).
@wm.on_key(
    key_definition=KeyDefinition(modifiers=KeyboardModifiers.Control, key=KeyDefinition.Key(name="d")),
    window_matcher=WindowMatchers(class_="URxvt"),
)
def close_urxvt(window: Window, event: XKeyEvent) -> None:
    window.close()


@wm.on_key_release(key_definition="Control + b")
def on_control_b_up(window: Window, event: XKeyEvent) -> None:
    print("Control + b released")


@wm.on_button(button=BUTTONS.Button1, modifiers=KeyboardModifiers.Control)
def on_ctrl_click(window: Window, event: XButtonEvent) -> None:
    print(event.x, event.y)


# `event` is None during the startup sweep (no real CreateNotify for windows that already existed).
@wm.on_create()
def on_any_create(window: Window, event: Optional[XCreateWindowEvent]) -> None:
    print(window.get_name_and_class())


@wm.on_manage(matcher=WindowMatchers(name="easyeffects", class_="easyeffects"))
def on_easyeffects(window: Window, event: XCreateWindowEvent) -> None:
    # .svg goes through resvg; any other extension (.png, .jpg, .ico, …) goes through ImageMagick
    window.set_icon(icon=Path("/path/to/icon.svg"))

    @wm.on_destroy(window=window)
    def on_easyeffects_gone(window: Window, event: XDestroyWindowEvent) -> None:
        print("easyeffects closed")


@wm.on_destroy()
def on_any_destroy(window: Window, event: XDestroyWindowEvent) -> None:
    print(f"destroyed {window}")


@wm.on_focus()
def on_focus(window: Window, event: XFocusChangeEvent) -> None:
    print(f"focus {window}")


@wm.on_unfocus()
def on_unfocus(window: Window, event: XFocusChangeEvent) -> None:
    print(f"unfocus {window}")


@wm.on_map()
def on_map(window: Window, event: XMapEvent) -> None:
    print(f"mapped {window}")


@wm.on_unmap()
def on_unmap(window: Window, event: XUnmapEvent) -> None:
    print(f"unmapped {window}")


@wm.on_configure()
def on_configure(window: Window, event: XConfigureEvent) -> None:
    print(window, event.width, event.height)


@wm.on_client_message(message_type="_NET_ACTIVE_WINDOW")
def on_active(window: Window, event: XClientMessageEvent) -> None:
    print(window, event.message_type)


@wm.on_timer(timeout=60.0)
def every_minute() -> Optional[bool]:
    print("tick")
    return None  # return True to stop


@wm.on_manage(matcher=WindowMatchers(name="Navigator", class_="firefox", window_type=["_NET_WM_WINDOW_TYPE_NORMAL"]))
def firefox_opened(window: Window, event: XCreateWindowEvent) -> None:
    n: Notification = Notification(
        app_name="Firefox",
        summary="Firefox is now open!",
        body="<b>Notification body</b>",
        actions=[Notification.Action(visible_name="Action #1", callback=lambda: print("Action #1"))],
        on_close=lambda: print("notification closed"),
        hints=Notification.Hints(urgency=Notification.Hints.Urgency.NORMAL),
    )
    n.show()

Then:

orcsome3
# or: python3 -m orcsome3

Useful flags: --config PATH, --log-file PATH, --log-level DEBUG, --version.

One process per $DISPLAY (lock under $XDG_RUNTIME_DIR/orcsome3/). Extra monitors on the same X server share that instance. A second X server (Chrome Remote Desktop, VNC, :1) is a separate process and does not steal grabs from :0. Saving the loaded config reloads it automatically (or send SIGUSR1). SIGINT / SIGTERM quit.

If the config file fails to load (syntax error, exception at import time), orcsome3 does not crash — it logs the exception and keeps running with no handlers installed until the next successful reload. This applies to the very first load too. A missing --config path is different and still exits at startup; a broken file at a real path does not. See the tray icon below for how that error is surfaced.

🖱️ Tray icon

A tray icon appears when your panel has a StatusNotifier (SNI) host — most do (KDE, LXQt, many tint2/Waybar/Polybar setups, etc.). If no host is on the session bus, there is no icon; signals still work. The icon is rendered straight from the packaged SVG into the D-Bus pixmap (via resvg, already a build dependency) — nothing is ever copied into ~/.local/share/icons or left behind on uninstall.

Right-click for the menu:

  • Config: <path> — the config file currently loaded (informational).
  • Error: ... — only shown while the config has a load error; same text as the icon's tooltip. The icon itself also switches to a distinct "error" variant (red dot) while this is active, and a desktop notification fires too if a notification daemon is running. Clears on the next successful reload.
  • Change config file... — only shown if zenity or kdialog is installed on your system; opens a file picker and switches to the selected config live (no restart).
  • Quit — sends SIGTERM.

🛠️ Develop

Command What it does
make native Download/build static libs and compile the Cython backend
make native-fast Re-cythonize/link only (needs a prior native)
make native-rebuild Wipe the lib cache and rebuild
make stubs Regenerate orcsome3_backend.pyi from the .pyx (needs the .so)
make format / make lint ruff, mypy, basedpyright, extra checks, stub --check
make test unittest in tests/ (X tests skip if the display cannot be opened; grab delivery prefers Xephyr/Xvfb)
make clean Build artifacts, orcsome3_built_libraries/, .so

Do not hand-edit generated .c files or orcsome3_built_libraries/.

python -m orcsome3.libs.build --build-dir . is the same as make native. --dynamic links system libev/gd/Magick instead of the static copies.

📄 License

MIT. See LICENSE.

Metadata

Release files for orcsome3 1.0.0

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

Source distribution (sdist)

Source distribution for orcsome3 1.0.0
File Size Uploaded
orcsome3-1.0.0.tar.gz 456.1 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for orcsome3 1.0.0
File
orcsome3-1.0.0-cp315-cp315-manylinux_2_28_x86_64.whl CPython 3.15 CPython 3.15 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp314-cp314-manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp313-cp313-manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp312-cp312-manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp311-cp311-manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp310-cp310-manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp39-cp39-manylinux_2_28_x86_64.whl CPython 3.9 CPython 3.9 Linux glibc 2.28+ x86-64 Details
orcsome3-1.0.0-cp38-cp38-manylinux_2_28_x86_64.whl CPython 3.8 CPython 3.8 Linux glibc 2.28+ x86-64 Details

Total release size: 135.8 MB

Release files / orcsome3-1.0.0.tar.gz

Download URL orcsome3-1.0.0.tar.gz
Size 456.1 kB
Tags Source
SHA-256 checksum
How to use checksums
8b02bb68af1b20e94845748caae7da6a56333b1fcdff0ef07d9916a16a31e0a8
BLAKE2b-256 checksum
How to use checksums
5936fb3afed6977ef207a22edbcac5183367981a94cd33da366e2cee5542ed9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp315-cp315-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp315-cp315-manylinux_2_28_x86_64.whl
Size 17.0 MB
Tags CPython 3.15 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
032dbefb19c2a8b6a2c4d4d38b3552677637de32edbb970fa727f139b8d5a56d
BLAKE2b-256 checksum
How to use checksums
e77a9bc5dc6ae28226cfb2efa2fda901f59423489fadde5ba30ef8d00969e90a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp314-cp314-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp314-cp314-manylinux_2_28_x86_64.whl
Size 16.9 MB
Tags CPython 3.14 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
e5502061d72ae0813c399f74bfc81402061ec8d2bd85a51550450106e020e151
BLAKE2b-256 checksum
How to use checksums
10a22988b177a3d3e74019e997ccd1f1486f85b1506c4b22aecdc0f6948a7e0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp313-cp313-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp313-cp313-manylinux_2_28_x86_64.whl
Size 17.0 MB
Tags CPython 3.13 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
58da0f2995a1d77bcb4e285d5762a097d0a38acb370bc70cb423c6ac4d6bdf97
BLAKE2b-256 checksum
How to use checksums
9b216b9b8a6a0b67c466e527c3314ae1aa61073ef35f8b9289fb3f1e6e66e11f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp312-cp312-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp312-cp312-manylinux_2_28_x86_64.whl
Size 17.0 MB
Tags CPython 3.12 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
12de965a10f26cd9e730185bb5c8560b8dfdccdfe27858d8843bfb272751a0ee
BLAKE2b-256 checksum
How to use checksums
1e470e6c39b9a57ed4dd3fb439d1810d5223e7bd3f563ecfbc60b4f44759329a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp311-cp311-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp311-cp311-manylinux_2_28_x86_64.whl
Size 17.0 MB
Tags CPython 3.11 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
2f48c3d674b76d6f3be51c52288f7bcbb57f6ce61d65bab4ce6efc9a3cd5af2d
BLAKE2b-256 checksum
How to use checksums
1498a8600a47e022a8266de54fae0b72daf6778956f347451bf29bd59f9f37a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp310-cp310-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp310-cp310-manylinux_2_28_x86_64.whl
Size 16.9 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
04bf00b842c0a8b1d9a28eff01827eb918e41f76ba86bb8d7b424a87ea511001
BLAKE2b-256 checksum
How to use checksums
e0fbc4d60bb40034da3a18dcdf0f3fc288a6459c1596fa43cd78ab85191ce6db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp39-cp39-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp39-cp39-manylinux_2_28_x86_64.whl
Size 16.9 MB
Tags CPython 3.9 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
962c761a7390b73a9ceaa580448c24f4c8e9e9d4b99f1d5a9f2fdb00a455f7fd
BLAKE2b-256 checksum
How to use checksums
54e5dce8b615977733d1b5ffa0f43acf68230104c5b6f5ee9108928ea7a369ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / orcsome3-1.0.0-cp38-cp38-manylinux_2_28_x86_64.whl

Download URL orcsome3-1.0.0-cp38-cp38-manylinux_2_28_x86_64.whl
Size 16.7 MB
Tags CPython 3.8 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
16675e08e9cd4359f5e845abcc93aeb81238cf6a1f283579601d782f37c704e9
BLAKE2b-256 checksum
How to use checksums
4d0057039c8dc43465a511c89efddf984e7b17767dd20a9edf4e0be12153a77c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

9 release files

0.2.0

1 release file

0.1

1 release file

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