# Advanced Paste – Python Scripts Advanced Paste supports user-defined Python scripts that transform clipboard content. Scripts are discovered automatically from a configurable folder and appear as actions in the Advanced Paste UI. ## Quick start 1. Open the scripts folder — by default `%LOCALAPPDATA%\Microsoft\PowerToys\AdvancedPaste\Scripts`. You can change this in **Settings → Advanced Paste → Python scripts → Scripts folder**. 2. Drop a `.py` file into the folder. 3. Define a `convert()` function (see [V2 Interface](#v2-interface-recommended)). 4. Open the Advanced Paste UI (`Win+Shift+V`) — your script will appear in the action list. ## V2 Interface (Recommended) The V2 interface is the simplest way to write scripts. You define a single `convert()` function that receives clipboard data as arguments and returns the result. **No platform-specific code needed.** ### Minimal example — reverse text: ```python # @advancedpaste:name Reverse Text def convert(text): """Reverse the clipboard text.""" return text[::-1] ``` That's it. No `import sys`, no `json.load`, no clipboard libraries. Advanced Paste handles all the plumbing. ### How it works 1. Advanced Paste reads the clipboard and serializes it as JSON. 2. A built-in runner inspects your `convert()` function signature. 3. Only the parameters your function declares are passed as keyword arguments. 4. Your function returns the result — Advanced Paste sets it on the clipboard and pastes. ### Parameter convention Declare only the parameters you need: | Parameter | Type | Content | |-----------|------|---------| | `text` | `str` | Clipboard text content | | `html` | `str` | Clipboard HTML content | | `image_path` | `str` | Path to temp PNG file of clipboard image | | `image` | `str` | Alias for `image_path` | | `file_paths` | `list[str]` | List of clipboard file paths | | `files` | `list[str]` | Alias for `file_paths` | | `file_path` | `str` | First file path (convenience for single-file) | | `work_dir` | `str` | Writable temp directory (cleaned up after execution) | | `format` | `list[str]` | Detected clipboard format names | **Format inference:** Advanced Paste infers which clipboard formats your script supports from the parameter names. A script with `def convert(text)` only appears when the clipboard has text. Use `**kwargs` to accept all formats. ### Return value convention | Return type | Effect | |-------------|--------| | `str` | Sets clipboard to text | | `dict` | Full control — must include `result_type` key (see [Output schema](#output-payload)) | | `pathlib.Path` | Sets clipboard to that file | | `list` of paths | Sets clipboard to multiple files | | `None` | No-op (clipboard unchanged) | ### More examples **Convert text to uppercase:** ```python # @advancedpaste:name Upper Case def convert(text): return text.upper() ``` **Convert image to grayscale:** ```python # @advancedpaste:name Grayscale Image # @advancedpaste:requires PIL=Pillow from PIL import Image from pathlib import Path def convert(image_path, work_dir): img = Image.open(image_path).convert("L") out = Path(work_dir) / "gray.png" img.save(out) return out ``` **Return HTML:** ```python # @advancedpaste:name Wrap in Code Block def convert(text): return { "result_type": "html", "html": f"
{text}",
"text": text, # fallback for apps that don't support HTML
}
```
**Accept any format with kwargs:**
```python
# @advancedpaste:name Debug Clipboard
def convert(**kwargs):
"""Show what's on the clipboard as formatted text."""
import json
return json.dumps(kwargs, indent=2, default=str)
```
## Header format
The only required header is `name`:
```python
# @advancedpaste:name My Script Name
```
### Optional tags
| Tag | Description |
|-----|-------------|
| `name` | **Required.** Display name shown in the Advanced Paste UI. |
| `desc` | Short description / tooltip. (Can also use the `convert()` docstring.) |
| `formats` | Override auto-detected formats. Comma-separated: `text`, `html`, `image`, `file`, `any`. |
| `requires` | Declare Python package dependencies (see [Declaring dependencies](#declaring-dependencies)). |
| `enabled` | Set to `false` to disable the script without deleting it. |
### Tags no longer needed in V2
| Tag | Why |
|-----|-----|
| `platform` | Eliminated — V2 scripts run identically on Windows and WSL. |
| `version` | Reserved, not useful in practice. |
## Legacy Interface (V1)
Scripts that do NOT define a `convert()` function are treated as V1 (legacy) scripts and
continue to work as before:
### Windows mode (`platform windows`)
The script runs directly and owns the clipboard via `win32clipboard`.
```python
# @advancedpaste:name Reverse text
# @advancedpaste:formats text
# @advancedpaste:platform windows
import win32clipboard
win32clipboard.OpenClipboard()
text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT, text[::-1])
win32clipboard.CloseClipboard()
```
### WSL / Linux mode (`platform linux`)
The script reads JSON from stdin and writes JSON to stdout.
```python
# @advancedpaste:name WSL Upper Case
# @advancedpaste:formats text
# @advancedpaste:platform linux
import sys, json
data = json.load(sys.stdin)
text = data.get("text", "")
json.dump({"result_type": "text", "text": text.upper()}, sys.stdout)
```
## Input/Output JSON Schema (for V1 Linux and advanced V2 dict returns)
### Input payload
```jsonc
{
"version": 2,
"format": ["text"], // array of detected clipboard format names
"work_dir": "C:\\Temp\\...", // writable temp directory
"text": "Hello, world!", // present when clipboard has text
"html": "Hello", // present when clipboard has HTML
"image_path": "C:\\...\\input.png", // present when clipboard has an image
"file_paths": ["C:\\...\\file.txt"] // present when clipboard has files
}
```
### Output payload
```jsonc
{
"result_type": "text", // "text" | "html" | "image" | "file" | "files"
"text": "HELLO, WORLD!", // for result_type "text"
"html": "HELLO", // for result_type "html"
"image_path": "C:\\...\\output.png", // for result_type "image"
"file_paths": ["C:\\...\\out.txt"] // for result_type "file"/"files"
}
```
## Declaring dependencies
Use `requires` to declare Python packages the script needs:
```python
# @advancedpaste:requires markitdown='markitdown[all]'
# @advancedpaste:requires cv2=opencv-python-headless numpy requests
```
Each token is either:
- **`import_name`** — the pip package is assumed to have the same name (e.g. `requests`).
- **`import_name=pip_package`** — when the import name differs from the pip package
(e.g. `cv2=opencv-python-headless`, `PIL=Pillow`).
### Automatic import detection
Advanced Paste also scans the script body for `import` and `from ... import` statements
and cross-references them against the Python standard library. Any non-stdlib import
that is not already installed triggers a prompt to install it automatically.
## Security — script trust
The first time a script is executed (or after it has been modified), Advanced Paste
shows a confirmation dialog. Upon approval the SHA-256 hash of the script is stored.
Subsequent runs of the unchanged file skip the dialog.
## Error handling
When a script fails, Advanced Paste extracts the Python traceback from stderr and
displays a user-friendly summary in the UI:
- **ModuleNotFoundError** — identifies the missing module and suggests installing it.
- **SyntaxError** — shows the file and line number.
- **Timeout** — shows the configured timeout value (default 30 s; configurable in Settings).
- **Other errors** — shows the last line of the traceback as a summary, with the full
traceback available in the expandable *Details* section.
## Settings
The following settings are available under **Settings → Advanced Paste → Python scripts**:
| Setting | Description | Default |
|---------|-------------|---------|
| Python interpreter | Path to the Python executable. Leave blank for auto-detection. | *(auto-detect)* |
| Scripts folder | Folder to scan for `.py` scripts. | `%LOCALAPPDATA%\Microsoft\PowerToys\AdvancedPaste\Scripts` |
## Tips
- Put reusable helper functions in a separate `.py` file without a `# @advancedpaste:name`
header — it will be ignored by the script discovery and can be imported by other scripts.
- The `work_dir` parameter points to a temporary directory that is cleaned up after execution.
Use it for intermediate files (e.g., image processing output).
- V2 scripts are testable from the command line:
```
echo {"text":"hello"} | python _runner.py my_script.py
```