Matchify
Matchify automatically converts eligible if/elif/else chains into
Python 3.10+ match statements while preserving runtime behavior and source
formatting.
Examples
Simple equality chain:
# Before
if x == 1:
print("one")
elif x == 2:
print("two")
else:
print("other")
# After
match x:
case 1:
print("one")
case 2:
print("two")
case _:
print("other")
isinstance with attributes:
# Before
if isinstance(node, Point) and node.x == 5:
print("x is 5")
elif isinstance(node, Point):
print("other point")
# After
match node:
case Point(x=5):
print("x is 5")
case Point():
print("other point")
Sequence patterns:
# Before
if len(point) == 2 and point[0] == 0 and point[1] == 1:
print("origin offset")
elif len(point) == 2 and point[0] == 1:
print("other pair")
# After
match point:
case 0, 1:
print("origin offset")
case 1, _:
print("other pair")
Nested patterns (isinstance inside sequences):
# Before
if len(x) == 2 and isinstance(x[0], Point) and x[1] == 2:
print("point and 2")
elif len(x) == 2 and x[0] == 1 and x[1] == 1:
print("ones")
# After
match x:
case Point(), 2:
print("point and 2")
case 1, 1:
print("ones")
Nested sequences:
# Before
if (
len(data) == 2
and len(data[0]) == 2
and data[0][0] == 1
and data[0][1] == 2
and data[1] == 3
):
print("nested list")
elif (
len(data) == 2
and isinstance(data[0], Point)
and len(data[1]) == 2
and data[1][0] == 0
and data[1][1] == 0
):
print("point with coordinates")
# After
match data:
case [1, 2], 3:
print("nested list")
case Point(), [0, 0]:
print("point with coordinates")
Class patterns with sequence attributes:
# Before
class Data:
def __init__(self, value):
self.value = value
obj = Data([1, 2, 3])
if (
isinstance(obj, Data)
and len(obj.value) == 3
and obj.value[0] == 1
and obj.value[1] == 2
and obj.value[2] == 3
):
print("data with list")
elif isinstance(obj, Data):
print("other data")
# After
class Data:
def __init__(self, value):
self.value = value
obj = Data([1, 2, 3])
match obj:
case Data(value=[1, 2, 3]):
print("data with list")
case Data():
print("other data")
Installation
Install "matchify" as a command-line tool with uv:
uv tool install matchify
Or run it without installing:
uvx matchify path/to/project/
Key Features
- Automatic conversion of if/elif/else chains to Python 3.10+ match statements
- Preserves formatting and code structure using LibCST
- Supports multiple pattern types:
- Literal comparisons (
x == 1,x == "value") - Identity checks (
x is None,x is True) - isinstance checks (
isinstance(x, MyClass)) - Class patterns with attributes (
isinstance(p, Point) and p.x == 5) - Sequence patterns (
len(x) == 2 and x[0] == 0 and x[1] == 1) - Nested sequences (
[[1, 2], 3]) - Sequence attributes in class patterns (
Data(value=[1, 2, 3])) - Or patterns for isinstance tuples (
isinstance(x, (int, float)))
- Literal comparisons (
- Parallel processing for fast conversion of large codebases
- Safe transformations - only converts when semantics are preserved
Usage
# Convert a single file
matchify --write path/to/file.py
# Convert all Python files in a directory
matchify --write path/to/project/
# Convert with verbose output
matchify --write path/to/project/ -v
# Review diffs without writing changes
matchify path/to/project/ --check
# Show eligible conversions as diffs while converting
matchify path/to/project/ --show --write
# Also preview conversions that need a missing --assume value
matchify path/to/project/ --show-all --check
# Use parallel processing (default: number of CPUs)
matchify path/to/project/ --write -j 8
# Enable one risky assumption explicitly
matchify path/to/project/ --write --assume pure-subjects
# Disable all risky assumptions
matchify path/to/project/ --write --safe
# Enable all risky assumptions
matchify path/to/project/ --write --risky
# Convert only chains that meet a stylistic threshold
matchify path/to/project/ --convert-if "isinstance_checks >= 2 or branches >= 4"
--write writes conversions and --check only reports them as diffs; the two
options cannot be combined. --check implies --show and exits with 1 if any
file would change. Explicit --show or --show-all without --write is a
preview-only dry run that still exits 0 when conversions are available. In an
interactive terminal, omitting --write, --check, and the show flags shows a
diff and asks for confirmation before writing. In a non-interactive shell,
choose --write, --check, or --show explicitly.
pre-commit
Matchify provides two pre-commit hooks.
Use matchify to automatically rewrite files, similar to the default Black
hook:
repos:
- repo: https://github.com/15r10nk/matchify
rev: v0.1.0
hooks:
- id: matchify
Use matchify-check to only report files that would be converted without
modifying them:
repos:
- repo: https://github.com/15r10nk/matchify
rev: v0.1.0
hooks:
- id: matchify-check
Risky assumptions
By default, Matchify enables no risky assumptions. --safe makes that explicit,
while --risky enables all available assumptions. See the
risky assumptions documentation
for the semantic tradeoffs and individual --assume names.
--check and --show review the currently eligible conversions as diffs.
--show --write prints the same diffs while converting. Both report how many
conversions were not shown because they need a missing --assume value and
point you to --show-all to preview them. --show-all additionally prints the
required --assume value and a separate diff for each group of conversions
unlocked by that assumption. For if/elif previews, both commands also print
the computed --convert-if metric variables for that conversion.
Selective conversion with --convert-if
By default, Matchify converts every eligible chain. This is equivalent to:
--convert-if True
Use --convert-if to choose a different threshold based on source and generated
metrics:
matchify path/to/project/ --convert-if "branches >= 4 and guard_conditions == 0"
Use --convert-if True if you want that default spelled out explicitly in
scripts or examples. See the
selective conversion documentation
for the expression syntax and complete metric reference.
Development
Development and repository-testing notes are in CONTRIBUTING.md.
Issues
If you encounter any problems, please report an issue along with a detailed description.
License
Distributed under the terms of the MIT license, "matchify" is free and open source software.
Metadata
Release files for matchify 0.3.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 | Provenance | |
|---|---|---|---|---|
| matchify-0.3.0.tar.gz | 186.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| matchify-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 227.2 kB
Release files / matchify-0.3.0.tar.gz
| Download URL | matchify-0.3.0.tar.gz |
|---|---|
| Size | 186.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1555673b6dc6619c908cf1dba9053a75f0c633c8e9367989c0072cd520430e97
|
|
BLAKE2b-256 checksum How to use checksums |
c42021cefd51c614b1140e63e5b1149be7fd08054a5ade1ce3ba20694134136d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Omarchy","version":"4.0.3","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / matchify-0.3.0-py3-none-any.whl
| Download URL | matchify-0.3.0-py3-none-any.whl |
|---|---|
| Size | 40.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
72497cfdfce95bed195d7e76b51332ac9ce144e79c54b42de51551b5befaef71
|
|
BLAKE2b-256 checksum How to use checksums |
c296be2bbee820cca92787a04178714442a59a52404c6e66b5920ff8608a6a75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Omarchy","version":"4.0.3","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for matchify 0.3.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.