Skip to main content

No project description provided

Project description

twat

Twardoch's Workstation Automation Toolkit. A minimal plugin host for Python: install small, focused packages and access them all through one namespace and one CLI.

twat does nothing on its own. Install plugins, and they snap into place.

How it works

Python has a built-in mechanism for packages to register themselves with a group name — called entry points. twat uses this to build a dynamic namespace: when you access twat.fs, it looks up the twat.plugins entry point named fs, loads the module, and hands it back. No configuration files, no registration steps beyond a normal pip install.

import twat

# twat-fs registered itself as a "twat.plugins" entry point named "fs"
files = twat.fs.list_directory(".")

# twat-cache registered as "cache"
@twat.cache.memoize()
def expensive():
    ...

The CLI works the same way. twat fs list /tmp finds the fs plugin, rewrites sys.argv so the plugin thinks it was called directly, and calls twat_fs.main().

Install

pip install twat

twat is an empty host. Install plugins separately:

pip install twat-fs twat-cache twat-os

CLI

twat --help          # show host usage
twat --list          # list installed plugin entry point names
twat <plugin_name> [args...]

# Examples:
twat --list
twat fs list /tmp
twat cache clear

twat --list reads only plugin entry point metadata, so it does not import plugin modules just to show what is installed. Dispatch still works by loading the selected plugin, rewriting sys.argv so the plugin sees twat.<plugin> as its executable name, and calling the plugin module's callable main().

Writing a plugin

No SDK required. A plugin is a normal Python package that declares one entry point.

1. Package structure

twat-myplugin/
├── src/
│   └── twat_myplugin/
│       ├── __init__.py    # Public API — everything here becomes twat.myplugin.*
│       └── __main__.py    # CLI handler — must expose a main() function
├── pyproject.toml
└── README.md

2. Register the entry point (pyproject.toml)

[project.entry-points."twat.plugins"]
myplugin = "twat_myplugin"

This is the only twat-specific line in your entire package.

3. Expose your API (__init__.py)

from importlib import metadata

try:
    __version__ = metadata.version(__name__)
except metadata.PackageNotFoundError:
    __version__ = "0.0.0-dev"

def do_something():
    return "result"

# The CLI dispatcher calls plugin.main() — expose it here
try:
    from .__main__ import main
except ImportError:
    def main() -> None:
        pass

__all__ = ["do_something", "main", "__version__"]

4. Handle CLI calls (__main__.py)

import sys

def main() -> None:
    # sys.argv[0] is already "twat.myplugin" when called via `twat myplugin`
    print(f"Hello from myplugin! Args: {sys.argv[1:]}")
    sys.exit(0)

if __name__ == "__main__":
    main()

Naming conventions

Thing Convention Example
pip package twat-<name> with hyphens twat-myplugin
entry point name lowercase, no prefix myplugin
Python module twat_<name> with underscores twat_myplugin

The entry point name becomes the attribute on twat and the subcommand in the CLI.

How plugin loading works

twat.__getattr__ is called whenever you access an attribute that doesn't exist on the module. It scans the twat.plugins entry point group for a matching name, loads the module, registers it in sys.modules as twat.<name> (so subsequent accesses skip the lookup), and returns it.

If no plugin matches, a PluginError is raised with a clear message.

The CLI entry point (twat.main) intercepts sys.argv, pulls out the plugin name, rewrites sys.argv[0] to twat.<plugin> and sys.argv[1:] to the remaining arguments, then calls the plugin's main(). The plugin never knows it was dispatched through twat.

Development

git clone https://github.com/twardoch/twat
cd twat
uv venv && uv sync
pytest -xvs

License

MIT

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

twat-2.7.14.tar.gz (20.1 kB view details)

Uploaded Source

Built Distribution

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

twat-2.7.14-py3-none-any.whl (18.0 kB view details)

Uploaded Python 3

File details

Details for the file twat-2.7.14.tar.gz.

File metadata

  • Download URL: twat-2.7.14.tar.gz
  • Upload date:
  • Size: 20.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for twat-2.7.14.tar.gz
Algorithm Hash digest
SHA256 adf22044f7902e4959394a743a672ee465f9a2c05856257d6b2d19a082749f81
MD5 b16c2eb9979e903c9abf9d95df3fe1da
BLAKE2b-256 85d54170301a66a4daa67021e1e2cc51230371d18d110bf2d796f5f7187c4f6a

See more details on using hashes here.

File details

Details for the file twat-2.7.14-py3-none-any.whl.

File metadata

  • Download URL: twat-2.7.14-py3-none-any.whl
  • Upload date:
  • Size: 18.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for twat-2.7.14-py3-none-any.whl
Algorithm Hash digest
SHA256 30ec4428e27c2773feff24eeea1c2b090950a1558734c191274b85541ca5793b
MD5 05c7a8b2e2a4bad5bb87fb9dc9915e8b
BLAKE2b-256 2a9c51c767960ec5efa144e264a2d49b44ed8695805b291306d2b8c952c7ad7e

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