Skip to main content

A Python implementation of TypeScript's ts-pattern, type-safe pattern matching with an expressive API

Project description

match-expression

A Python implementation of TypeScript's ts-pattern, bringing powerful, type-safe pattern matching to Python with an expressive, chainable API.

Features

  • Chainable API: Intuitive match(value).case(pattern, then).exhaustive() syntax
  • Type-safe: Full type inference support with pyright/mypy
  • Exhaustiveness checking: Ensures all cases are handled at compile time
  • Zero dependencies: Lightweight and fast
  • Pythonic: Leverages Python 3.10+ type system features

Installation

pip install match_expression

Quick Start

from typing import Literal
from py_pattern import match

# Literal type matching
def process_status(status: Literal["pending", "success", "error"]) -> int:
    return (
        match(status)
        .case("pending", 0)
        .case("success", 1)
        .case("error", -1)
        .exhaustive()
    )

# Type matching with classes
class Dog:
    def bark(self) -> str:
        return "Woof!"

class Cat:
    def meow(self) -> str:
        return "Meow!"

def handle_animal(animal: Dog | Cat) -> str:
    return (
        match(animal)
        .case(Dog, lambda d: d.bark())
        .case(Cat, lambda c: c.meow())
        .exhaustive()
    )

Examples

Literal Type Matching

from typing import Literal
from py_pattern import match

type Platform = Literal["web", "mobile", "desktop"]

def get_app_name(platform: Platform) -> str:
    return (
        match(platform)
        .case("web", "Web Application")
        .case("mobile", "Mobile App")
        .case("desktop", "Desktop Software")
        .exhaustive()
    )

# Type checker knows all cases are covered!

Class Type Matching

from py_pattern import match

class Success:
    def __init__(self, value: str):
        self.value = value

class Error:
    def __init__(self, message: str):
        self.message = message

def handle_result(result: Success | Error) -> str:
    return (
        match(result)
        .case(Success, lambda s: f"Success: {s.value}")
        .case(Error, lambda e: f"Error: {e.message}")
        .exhaustive()
    )

Using otherwise for Default Cases

from py_pattern import match

def classify_number(n: int) -> str:
    return (
        match(n)
        .case(0, "zero")
        .case(1, "one")
        .case(2, "two")
        .otherwise("many")
    )

Mixed Return Types

The library correctly infers union return types:

from py_pattern import match

def process(value: int | str) -> int | str:
    return (
        match(value)
        .case(int, lambda i: i * 2)      # Returns int
        .case(str, lambda s: s.upper())  # Returns str
        .exhaustive()
    )
    # Type is inferred as: int | str

API Reference

match(value: V) -> Match[V]

Starts a pattern matching chain.

.case(pattern: P, then: R) -> Case[V, P, R]

Matches against a pattern. If the pattern matches, executes then.

  • pattern: A value to match against (for literals) or a type (for isinstance checks)
  • then: The value to return or a function to execute with the matched value

.exhaustive() -> R

Ensures all cases are handled. Raises ExhaustiveError if not all cases are covered.

.otherwise(default: R) -> R

Provides a default value for unmatched cases.

Type Checking

The library is designed to work with type checkers like pyright and mypy:

# Install pyright
pip install pyright

# Type check your code
pyright your_file.py

Contributing

Contributions are welcome! Here's how to get started:

  1. Clone the repository
git clone https://github.com/qodot/match-expression.git
cd match-expression
  1. Install development dependencies
uv sync --dev
  1. Run tests
uv run pytest
  1. Type check
uv run pyright src/ tests/

Requirements

  • Python 3.10 or higher
  • No external dependencies

License

MIT License - see LICENSE file for details

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

match_expression-0.1.1.tar.gz (5.7 kB view details)

Uploaded Source

Built Distribution

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

match_expression-0.1.1-py3-none-any.whl (4.8 kB view details)

Uploaded Python 3

File details

Details for the file match_expression-0.1.1.tar.gz.

File metadata

  • Download URL: match_expression-0.1.1.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.10

File hashes

Hashes for match_expression-0.1.1.tar.gz
Algorithm Hash digest
SHA256 150173e6f86867d914ffe1420c8698dedc24e224c1e7d1db1eef80100f75487d
MD5 3c1d3bda3c103342e05cba1b0c259b2d
BLAKE2b-256 e9b6da2f761611b90a9fb33f1ac5a56474cf762d590fb641f2ec2165062a60c2

See more details on using hashes here.

File details

Details for the file match_expression-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for match_expression-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 358786fc8d41de4280a76ff9af3ccb5d8451c16836ebf670481d4837195b6790
MD5 fe89ca596a261a607f43538bc4d22177
BLAKE2b-256 199013b1a15c8ef68ffe1814c8da10c996f6a798f013e7c5026748f49553d280

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page