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: WhenTrue(default), evaluates callable functions. WhenFalse, 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 matcheval: WhenTrue(default), evaluates callable functions. WhenFalse, 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:
- Clone the repository
git clone https://github.com/qodot/match-expression.git
cd match-expression
- Install development dependencies
uv sync --dev
- Run tests
uv run pytest
- 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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0350704ac9ee31bd203ceba1a389f6c5c5afc22e4fd45cc869e0821c76ead12
|
|
| MD5 |
12100d99cb0166b6b2ee657029161da8
|
|
| BLAKE2b-256 |
6e3f3f8078775e3bf0041810eff493972ceaefb52fe21102151ca29b4f67601f
|
File details
Details for the file match_expression-0.2.0-py3-none-any.whl.
File metadata
- Download URL: match_expression-0.2.0-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4864f43fccf3ae313e2819743d82d11dde0d6626b5ec68b0d1313e909de02452
|
|
| MD5 |
998e187849ea2e6b90fc1f0d397b44b5
|
|
| BLAKE2b-256 |
3b1893a1a2837c425b07ead8f41084f806f297dcb7f846a895515a9cca7b3512
|