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.12+ 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

Delayed Evaluation with eval=False

You can defer the evaluation of callable functions by using eval=False:

from py_pattern import match
from typing import Callable

def get_handler(command: str) -> Callable[[], str]:
    return (
        match(command)
        .case("start", lambda: "Starting application...")
        .case("stop", lambda: "Stopping application...")
        .case("restart", lambda: "Restarting application...")
        .exhaustive(eval=False)  # Returns the lambda without calling it
    )

# Get the handler function without executing it
handler = get_handler("start")
# Execute later when needed
result = handler()  # "Starting application..."

This is useful when you want to:

  • Return handler functions for later execution
  • Implement lazy evaluation patterns
  • Build command dispatch systems

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(eval: bool = True) -> R

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

  • eval: When True (default), evaluates callable functions. When False, returns the callable without evaluating it.

.otherwise(default: R, eval: bool = True) -> R

Provides a default value for unmatched cases.

  • default: The value to return or a function to execute when no patterns match
  • eval: When True (default), evaluates callable functions. When False, returns the callable without evaluating it.

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.12 or higher
  • No external dependencies

Special Thanks

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.2.0.tar.gz (5.9 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.2.0-py3-none-any.whl (5.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for match_expression-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c0350704ac9ee31bd203ceba1a389f6c5c5afc22e4fd45cc869e0821c76ead12
MD5 12100d99cb0166b6b2ee657029161da8
BLAKE2b-256 6e3f3f8078775e3bf0041810eff493972ceaefb52fe21102151ca29b4f67601f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for match_expression-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4864f43fccf3ae313e2819743d82d11dde0d6626b5ec68b0d1313e909de02452
MD5 998e187849ea2e6b90fc1f0d397b44b5
BLAKE2b-256 3b1893a1a2837c425b07ead8f41084f806f297dcb7f846a895515a9cca7b3512

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