"Extend Pillow with custom image plugins, diagnose plugin
Scanned 9/8/2026
Install to Claude Code
npx -y skills add VectorSpaceLab/AREX-Skill --skill plugins-and-extension --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Plugins And Extension?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/vectorspacelab-plugins-and-extension)More formats (shields.io, HTML) on the badges page.
---
name: plugins-and-extension
description: "Extend Pillow with custom image plugins, diagnose plugin
registration/open dispatch problems, and inspect optional codec/feature
support for advanced integrations."
disable-model-invocation: true
metadata:
disco-role: operating
license: NOASSERTION
---
# Pillow Plugins and Extension
Use this sub-skill when you need to add a custom Pillow image plugin, debug why `Image.open()` is not dispatching to a plugin, understand the boundary between Python plugin code and decoder backends, or inspect optional feature/codec support. For everyday open/save usage and metadata options, use the root skill's formats guidance instead.
## Core Model
Pillow image loading is plugin-driven:
1. A plugin module registers a format with `PIL.Image.register_open(format_id, factory, accept)`.
2. `Image.open(fp, mode="r", formats=None)` reads the first 16 bytes and calls each loaded plugin's `accept(prefix)` function in registry order.
3. When `accept` returns truthy, Pillow constructs the registered `ImageFile.ImageFile` subclass and calls its `_open()` method.
4. `_open()` parses only enough header data to set image metadata and `tile` descriptors; actual pixels are decoded later when `load()`, pixel access, conversion, or saving needs the data.
5. `ImageFile.load()` sorts tile descriptors, creates the requested decoder, seeks to tile offsets, and feeds bytes to the decoder.
Important consequence: a new plugin must be imported before it can participate in dispatch. Pillow does not automatically import arbitrary `*ImagePlugin.py` files from `sys.path`.
## Minimal Plugin Checklist
For a read plugin:
- Create a module named like `XxxImagePlugin.py` or another importable module name used by your package.
- Define a narrow, cheap `_accept(prefix: bytes)` that inspects only the available prefix bytes.
- Subclass `PIL.ImageFile.ImageFile`.
- Set class attributes `format = "FORMAT"` and `format_description = "Human description"`.
- In `_open()`, validate the header, set `self._size = (width, height)`, set `self._mode = "L"`, `"RGB"`, or another valid Pillow mode, and set `self.tile` if the image can be loaded.
- Register with `Image.register_open(format, ImageFileSubclass, _accept)`.
- Register extensions with `Image.register_extension(format, ".ext")` or `Image.register_extensions(format, [".ext", ...])`.
- Import the plugin before calling `Image.open()` unless it is packaged so the application imports it during startup.
Use `scripts/minimal_spam_plugin.py` as a self-contained working example. It registers a trivial raw SPAM format and has `--self-test` to create and open a tiny fixture without relying on repository files.
## Tile Boundary
A tile descriptor tells `ImageFile.load()` how to decode bytes into a rectangular region. Use `ImageFile._Tile(codec_name, extents, offset, args)`:
- `codec_name`: a decoder name such as `"raw"`, `"jpeg"`, `"zip"`, or a registered Python decoder name.
- `extents`: `(left, top, right, bottom)` in image coordinates, usually `(0, 0) + self.size` for one full-image tile.
- `offset`: byte offset from the start of the file to the encoded image data.
- `args`: decoder-specific parameters. For the raw decoder, use `(raw_mode, stride, orientation)`; common simple case is `(self.mode, 0, 1)`.
Python plugins can parse headers and route bytes to existing decoders. They cannot magically add support for a compressed bitstream that Pillow has no decoder for; use an existing decoder, implement a Python `ImageFile.PyDecoder`, or provide/enable a C extension codec when performance or codec availability requires it.
## Feature and CLI Inspection
Use `PIL.features` and `python -m PIL` to diagnose environment-dependent support:
```python
from PIL import features
print(features.check("jpg"))
print(features.check("webp"))
print(features.version("littlecms2"))
print(features.get_supported())
```
- `python -m PIL` prints Pillow build information and supported formats.
- `python -m PIL --report` prints the report without the verbose supported-format listing.
- `features.check(name)` accepts module, codec, or feature names such as `pil`, `freetype2`, `littlecms2`, `webp`, `avif`, `jpg`, `jpg_2000`, `zlib`, `libtiff`, `raqm`, `libjpeg_turbo`, and `xcb`.
- Optional codec absence explains failures for plugins that delegate to compiled decoders, for example JPEG 2000, TIFF, WebP, or AVIF.
## Debugging Flow
1. Confirm the plugin module is imported before `Image.open()`.
2. Check the format id is uppercase-consistent in registrations and class `format`.
3. Check `_accept(prefix)` returns truthy for a valid file and falsey for unrelated files.
4. Open with `formats=["FORMAT"]` to isolate registry-order effects.
5. Confirm `_open()` sets valid `_size`, `_mode`, and `tile` before returning.
6. Confirm tile offset and raw mode match the file layout.
7. Call `im.load()` in tests so decoder errors appear instead of only header-identification success.
8. Use `python -m PIL` or `features.check()` when the plugin depends on optional compiled support.
## References
- `references/custom-plugin-pattern.md` gives the authoring pattern and packaging/import guidance.
- `references/plugin-api-reference.md` summarizes the registry, `ImageFile`, tile, decoder, and features APIs.
- `references/troubleshooting.md` maps common plugin failures to fixes.
- `scripts/minimal_spam_plugin.py` is an executable reference plugin and self-test.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!