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 path/to/file.py
# Convert all Python files in a directory
matchify path/to/project/
# Convert with verbose output
matchify path/to/project/ -v
# Check whether files would be converted without writing changes
matchify path/to/project/ --check
# Use parallel processing (default: number of CPUs)
matchify path/to/project/ -j 8
# Enable one risky assumption explicitly
matchify path/to/project/ --assume pure-subjects
# Disable all risky assumptions
matchify path/to/project/ --safe
# Enable all risky assumptions
matchify path/to/project/ --risky
By default, Matchify enables no risky assumptions. --safe makes that explicit.
--risky enables all available risky assumptions.
When a skipped if/elif chain would require a risky assumption, the CLI
prints the file location and the required --assume value instead of converting
that chain.
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.0.1
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.0.1
hooks:
- id: matchify-check
Available risky assumptions:
pure-subjects: permits transformations such asa.x == 1 and b.y == 2into a match on(a.x, b.y). This evaluates every subject eagerly, so enable it only when those name, attribute, and subscript reads cannot raise exceptions or produce observable side effects. Without the option, laterandoperands remain guards and preserve short-circuiting.use-object: permits generic attribute patterns such asobject(x=1)when different branches inspect attributes of a common object without an explicitisinstancecheck. This performs pattern-time attribute lookups, so enable it only when those lookups cannot raise exceptions or produce observable side effects.identity-equality: permits conversions from qualified identity comparisons such asop is Op.ADDto value patterns such ascase Op.ADD. Match value patterns compare with equality, not identity, so enable it only when identity and equality are equivalent for those values.list-sequence-pattern: permits a sequence pattern to imply an explicitisinstance(value, list)check. Python sequence patterns can also match other sequence types, so enable it only when that broader match is acceptable.tuple-sequence-pattern: permits a sequence pattern to imply an explicitisinstance(value, tuple)check. Python sequence patterns can also match other sequence types, so enable it only when that broader match is acceptable. Checks against(list, tuple)require both sequence assumptions.lookup-equality: permits dictionary lookup tables embedded in statements to becomematchstatements. Dictionary lookup uses hashing while patterns use equality, and dictionary values are evaluated only in the selected case instead of eagerly when constructing the dictionary. Enable it only when those equality and evaluation-order differences are acceptable. Tuple keys, including nested tuples, become sequence patterns and can therefore also match equivalent non-tuple sequences.
Development and repository-testing notes are in CONTRIBUTING.md.
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")
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file matchify-0.1.0.tar.gz.
File metadata
- Download URL: matchify-0.1.0.tar.gz
- Upload date:
- Size: 157.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74582ebcdd206945d6ec6fedd232da3cbb0141c237e2b223de885d8b49bdd33b
|
|
| MD5 |
90d9e69996f6ff8fde7f0d781fea0284
|
|
| BLAKE2b-256 |
80e57729e71a636af86772b96806d53866ee8b2bc35fde85ced197b71099a0b2
|
Provenance
The following attestation bundles were made for matchify-0.1.0.tar.gz:
Publisher:
ci.yml on 15r10nk/matchify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
matchify-0.1.0.tar.gz -
Subject digest:
74582ebcdd206945d6ec6fedd232da3cbb0141c237e2b223de885d8b49bdd33b - Sigstore transparency entry: 2341231729
- Sigstore integration time:
-
Permalink:
15r10nk/matchify@d174a435c0c2caf4c016b67c3d804779e05f347f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/15r10nk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d174a435c0c2caf4c016b67c3d804779e05f347f -
Trigger Event:
push
-
Statement type:
File details
Details for the file matchify-0.1.0-py3-none-any.whl.
File metadata
- Download URL: matchify-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ab864ff476296fe7d67c951417e13b47a0b72563ef7c53280e23450c4b82392
|
|
| MD5 |
388dbcf65f5d2f83d29092025432f08f
|
|
| BLAKE2b-256 |
da474f24d6bd9650973537576136b3ec12b4a31ffbb304e0efc0a1d251b72866
|
Provenance
The following attestation bundles were made for matchify-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on 15r10nk/matchify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
matchify-0.1.0-py3-none-any.whl -
Subject digest:
6ab864ff476296fe7d67c951417e13b47a0b72563ef7c53280e23450c4b82392 - Sigstore transparency entry: 2341231733
- Sigstore integration time:
-
Permalink:
15r10nk/matchify@d174a435c0c2caf4c016b67c3d804779e05f347f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/15r10nk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d174a435c0c2caf4c016b67c3d804779e05f347f -
Trigger Event:
push
-
Statement type: