Skip to main content

ci pypi version Python Versions PyPI - Downloads GitHub Sponsors

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)))
  • 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

# 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.

Available risky assumptions:

  • pure-subjects: permits transformations such as a.x == 1 and b.y == 2 into 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, later and operands remain guards and preserve short-circuiting.
  • use-object: permits generic attribute patterns such as object(x=1) when different branches inspect attributes of a common object without an explicit isinstance check. This performs pattern-time attribute lookups, so enable it only when those lookups cannot raise exceptions or produce observable side effects.

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

matchify-0.0.1.tar.gz (77.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

matchify-0.0.1-py3-none-any.whl (25.7 kB view details)

Uploaded Python 3

File details

Details for the file matchify-0.0.1.tar.gz.

File metadata

  • Download URL: matchify-0.0.1.tar.gz
  • Upload date:
  • Size: 77.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for matchify-0.0.1.tar.gz
Algorithm Hash digest
SHA256 49fc5d007f1805aecc6097cb1f51733ff454f0f16a6f3465b80b180828ea24cf
MD5 36ea9c900f9779c52cd3ab6052baa727
BLAKE2b-256 6897918df60a2a7d252b673387616b7cb90544da180563762bca7d6b481f1a43

See more details on using hashes here.

File details

Details for the file matchify-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: matchify-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 25.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for matchify-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cf5ce369995d0b77d5b02d03fd5b26ee705e00f39478aaded154d17b47b6480d
MD5 583566d23ca588d7a9f1a2e42f3187eb
BLAKE2b-256 13693e2bcd4ada2c9d7b31ae6312bb48d9bb200c4f8ced9a40218e81e2094e38

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

0.1.0

2 files

This release

0.0.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page