Smithy
Free Python RPA engine — create automation bots with simple async API.
Quick Start
import asyncio
from smithy import Smithy
from smithy.windows.tools.process import ProcessTool
from smithy.windows.tools.click import ClickTool
from smithy.windows.tools.wait import WaitTool
from smithy.windows.tools.delay import DelayTool
from smithy.windows.tools.screenshot import ScreenshotTool
from smithy.windows.tools.input_text import InputTextTool
from smithy.windows.tools.keyboard import KeyboardTool
from smithy.windows.tools.set_text import SetTextTool
from smithy.windows.tools.get_element import GetElementTool
bot = Smithy(
tools=[
ProcessTool(),
ClickTool(),
WaitTool(),
DelayTool(),
ScreenshotTool(),
InputTextTool(),
KeyboardTool(),
SetTextTool(),
GetElementTool(),
]
)
async def main() -> None:
app = await bot.process_run("notepad.exe")
await bot.wait(app, class_name="Notepad", name="*Notepad")
await bot.click(app, name="File")
await bot.delay(duration_ms=300)
await bot.click(app, name="Save As...")
await bot.input_text(app, text="hello world")
await bot.keyboard(keys="[CTRL]S")
await bot.screenshot("notepad.png")
await bot.process_stop(app)
asyncio.run(main())
Built-in Tools
- ProcessTool (
windows.process) — launch and stop Windows processes by name - ClickTool (
windows.click) — click a UI element by selector or context key - WaitTool (
windows.wait) — poll until a UI element appears (with timeout) - DelayTool (
windows.delay) — pause execution for a fixed duration - ScreenshotTool (
windows.screenshot) — capture the screen or a window to a file - InputTextTool (
windows.input_text) — type plain text into a UI element - KeyboardTool (
windows.keyboard) — send key combos and presses (e.g."[CTRL]S","[CTRL!]","[ENTER]") - SetTextTool (
windows.set_text) — replace a UI element's text programmatically (ValuePattern / WM_SETTEXT) - GetElementTool (
windows.get_element) — read a UI element's attributes as a dict
All UI tools accept optional pid (or a ProcessHandle) to scope element search to a specific window.
Custom Tools
Create tools from simple async functions:
from smithy import Smithy, tool
@tool("greet", description="Greet a person")
async def greet(config: dict) -> dict:
name = config.get("name", "World")
return {"message": f"Hello, {name}!"}
bot = Smithy(tools=[greet])
async def main() -> None:
result = await bot.call("greet", name="Alice")
print(result["message"]) # Hello, Alice!
asyncio.run(main())
Error Handling
from smithy.core.errors import InvalidInput, ElementNotFound, PlatformError
try:
await bot.click(app, name="Nonexistent")
except ElementNotFound:
print("Element not found")
except PlatformError as e:
print(f"Platform error: {e}")
Selector Capture
A dev utility for inspecting UI elements at screen coordinates and generating tool configs:
pip install smithy[capture]
# Single capture mode
python -m smithy.windows.tools.selector_capture single -o selectors.json
# Series mode — auto-record clicks and typing
python -m smithy.windows.tools.selector_capture series -o recording.json
# Interactive record mode
python -m smithy.windows.tools.selector_capture record -o flow.json
Install
pip install smithy # core (no deps)
pip install smithy[windows] # Windows UIA tools
pip install smithy[capture] # selector capture (pynput + pyperclip)
pip install smithy[all] # everything
pip install -e ".[dev]" # development
Development
# Using uv (recommended)
uv venv .venv
.venv\Scripts\activate
uv pip install -e ".[dev,windows,capture]"
# Or with pip
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev,windows,capture]"
pytest # run tests
ruff check src/ tests/ # linter
mypy src/smithy --strict # type check
Project Structure
src/smithy/
├── __init__.py — Public API: Smithy, ProcessHandle, Tool, errors
├── facade.py — Smithy facade (async tool dispatch)
├── core/
│ ├── tool.py — Tool protocol, AbstractTool, @tool decorator
│ ├── registry.py — ToolRegistry (name → tool dispatch)
│ ├── events.py — EventBus, ToolEvent, Middleware
│ └── errors.py — Error hierarchy (ToolError, ElementNotFound, etc.)
└── windows/
├── element.py — SafeUIElement (thread-safe COM wrapper)
├── selector.py — ElementSelector (UIA tree search)
└── tools/
├── process.py — ProcessTool
├── click.py — ClickTool
├── wait.py — WaitTool
├── delay.py — DelayTool
├── screenshot.py — ScreenshotTool
├── input_text.py — InputTextTool
├── set_text.py — SetTextTool
├── get_element.py — GetElementTool
├── _resolve.py — Shared element resolution helper
└── selector_capture/ — Dev tool for UI inspection
Examples
examples/basic_bot.py— Launch Notepad and interact with its UIexamples/custom_tool.py— Create and use custom tools
License
MIT
Metadata
Release files for smithy-py 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| smithy_py-0.1.0.tar.gz | 83.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| smithy_py-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 124.5 kB
Release files / smithy_py-0.1.0.tar.gz
| Download URL | smithy_py-0.1.0.tar.gz |
|---|---|
| Size | 83.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
face361be487799549e023e7d1214f59e6afeaf30f11a3f2561526d4b4fd0ddf
|
|
BLAKE2b-256 checksum How to use checksums |
7271ed95816fefa68c19cd34c0521a948227d22eaa73e6987ace13b8df5778c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / smithy_py-0.1.0-py3-none-any.whl
| Download URL | smithy_py-0.1.0-py3-none-any.whl |
|---|---|
| Size | 41.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eb9aef02b4ea2659f362afa64f5a0a00e8498af6a5f2419043915558c5b75672
|
|
BLAKE2b-256 checksum How to use checksums |
d6e2c015ce83ed0c4cba2a6235f90415d8ecb3091359e2b609fe9d4c294a344c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|