match-expression


Namematch-expression JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryA Python implementation of TypeScript's ts-pattern, type-safe pattern matching with an expressive API
upload_time2025-09-03 02:52:49
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseMIT
keywords exhaustive functional match pattern pattern-matching ts-pattern type-safe typescript
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # match-expression

A Python implementation of TypeScript's [ts-pattern](https://github.com/gvergnaud/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

```bash
pip install match_expression
```

## Quick Start

```python
from typing import Literal
from match_expression 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

```python
from typing import Literal
from match_expression 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

```python
from match_expression 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

```python
from match_expression 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:

```python
from match_expression 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`:

```python
from match_expression 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:

```bash
# 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
```bash
git clone https://github.com/qodot/match-expression.git
cd match-expression
```

2. Install development dependencies
```bash
uv sync --dev
```

3. Run tests
```bash
uv run pytest
```

4. Type check
```bash
uv run pyright src/ tests/
```

## Requirements

- Python 3.12 or higher
- No external dependencies

## Special Thanks
- [@code-yeongyu](https://github.com/code-yeongyu) and [@indentcorp](https://github.com/indentcorp)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "match-expression",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "exhaustive, functional, match, pattern, pattern-matching, ts-pattern, type-safe, typescript",
    "author": null,
    "author_email": "Qodot <qodot@example.com>",
    "download_url": "https://files.pythonhosted.org/packages/08/af/18d19aca9f4065c9da481280f81e8276e3a4325e2536f59fa219ed8d8d1d/match_expression-0.3.0.tar.gz",
    "platform": null,
    "description": "# match-expression\n\nA Python implementation of TypeScript's [ts-pattern](https://github.com/gvergnaud/ts-pattern), bringing powerful, type-safe pattern matching to Python with an expressive, chainable API.\n\n## Features\n\n- **Chainable API**: Intuitive `match(value).case(pattern, then).exhaustive()` syntax\n- **Type-safe**: Full type inference support with pyright/mypy\n- **Exhaustiveness checking**: Ensures all cases are handled at compile time\n- **Zero dependencies**: Lightweight and fast\n- **Pythonic**: Leverages Python 3.12+ type system features\n\n## Installation\n\n```bash\npip install match_expression\n```\n\n## Quick Start\n\n```python\nfrom typing import Literal\nfrom match_expression import match\n\n# Literal type matching\ndef process_status(status: Literal[\"pending\", \"success\", \"error\"]) -> int:\n    return (\n        match(status)\n        .case(\"pending\", 0)\n        .case(\"success\", 1)\n        .case(\"error\", -1)\n        .exhaustive()\n    )\n\n# Type matching with classes\nclass Dog:\n    def bark(self) -> str:\n        return \"Woof!\"\n\nclass Cat:\n    def meow(self) -> str:\n        return \"Meow!\"\n\ndef handle_animal(animal: Dog | Cat) -> str:\n    return (\n        match(animal)\n        .case(Dog, lambda d: d.bark())\n        .case(Cat, lambda c: c.meow())\n        .exhaustive()\n    )\n```\n\n## Examples\n\n### Literal Type Matching\n\n```python\nfrom typing import Literal\nfrom match_expression import match\n\ntype Platform = Literal[\"web\", \"mobile\", \"desktop\"]\n\ndef get_app_name(platform: Platform) -> str:\n    return (\n        match(platform)\n        .case(\"web\", \"Web Application\")\n        .case(\"mobile\", \"Mobile App\")\n        .case(\"desktop\", \"Desktop Software\")\n        .exhaustive()\n    )\n\n# Type checker knows all cases are covered!\n```\n\n### Class Type Matching\n\n```python\nfrom match_expression import match\n\nclass Success:\n    def __init__(self, value: str):\n        self.value = value\n\nclass Error:\n    def __init__(self, message: str):\n        self.message = message\n\ndef handle_result(result: Success | Error) -> str:\n    return (\n        match(result)\n        .case(Success, lambda s: f\"Success: {s.value}\")\n        .case(Error, lambda e: f\"Error: {e.message}\")\n        .exhaustive()\n    )\n```\n\n### Using `otherwise` for Default Cases\n\n```python\nfrom match_expression import match\n\ndef classify_number(n: int) -> str:\n    return (\n        match(n)\n        .case(0, \"zero\")\n        .case(1, \"one\")\n        .case(2, \"two\")\n        .otherwise(\"many\")\n    )\n```\n\n### Mixed Return Types\n\nThe library correctly infers union return types:\n\n```python\nfrom match_expression import match\n\ndef process(value: int | str) -> int | str:\n    return (\n        match(value)\n        .case(int, lambda i: i * 2)      # Returns int\n        .case(str, lambda s: s.upper())  # Returns str\n        .exhaustive()\n    )\n    # Type is inferred as: int | str\n```\n\n### Delayed Evaluation with `eval=False`\n\nYou can defer the evaluation of callable functions by using `eval=False`:\n\n```python\nfrom match_expression import match\nfrom typing import Callable\n\ndef get_handler(command: str) -> Callable[[], str]:\n    return (\n        match(command)\n        .case(\"start\", lambda: \"Starting application...\")\n        .case(\"stop\", lambda: \"Stopping application...\")\n        .case(\"restart\", lambda: \"Restarting application...\")\n        .exhaustive(eval=False)  # Returns the lambda without calling it\n    )\n\n# Get the handler function without executing it\nhandler = get_handler(\"start\")\n# Execute later when needed\nresult = handler()  # \"Starting application...\"\n```\n\nThis is useful when you want to:\n- Return handler functions for later execution\n- Implement lazy evaluation patterns\n- Build command dispatch systems\n\n## API Reference\n\n### `match(value: V) -> Match[V]`\nStarts a pattern matching chain.\n\n### `.case(pattern: P, then: R) -> Case[V, P, R]`\nMatches against a pattern. If the pattern matches, executes `then`.\n\n- `pattern`: A value to match against (for literals) or a type (for isinstance checks)\n- `then`: The value to return or a function to execute with the matched value\n\n### `.exhaustive(eval: bool = True) -> R`\nEnsures all cases are handled. Raises `ExhaustiveError` if not all cases are covered.\n\n- `eval`: When `True` (default), evaluates callable functions. When `False`, returns the callable without evaluating it.\n\n### `.otherwise(default: R, eval: bool = True) -> R`\nProvides a default value for unmatched cases.\n\n- `default`: The value to return or a function to execute when no patterns match\n- `eval`: When `True` (default), evaluates callable functions. When `False`, returns the callable without evaluating it.\n\n## Type Checking\n\nThe library is designed to work with type checkers like pyright and mypy:\n\n```bash\n# Install pyright\npip install pyright\n\n# Type check your code\npyright your_file.py\n```\n\n## Contributing\n\nContributions are welcome! Here's how to get started:\n\n1. Clone the repository\n```bash\ngit clone https://github.com/qodot/match-expression.git\ncd match-expression\n```\n\n2. Install development dependencies\n```bash\nuv sync --dev\n```\n\n3. Run tests\n```bash\nuv run pytest\n```\n\n4. Type check\n```bash\nuv run pyright src/ tests/\n```\n\n## Requirements\n\n- Python 3.12 or higher\n- No external dependencies\n\n## Special Thanks\n- [@code-yeongyu](https://github.com/code-yeongyu) and [@indentcorp](https://github.com/indentcorp)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Python implementation of TypeScript's ts-pattern, type-safe pattern matching with an expressive API",
    "version": "0.3.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/qodot/match-expression/issues",
        "Homepage": "https://github.com/qodot/match-expression",
        "Repository": "https://github.com/qodot/match-expression"
    },
    "split_keywords": [
        "exhaustive",
        " functional",
        " match",
        " pattern",
        " pattern-matching",
        " ts-pattern",
        " type-safe",
        " typescript"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7ed02f4f4d0c9c5ad5c63235816a7500b6f3844c271f98cecab30c07933fd9c3",
                "md5": "b0936d4cfb0adc9caa7a289205aa8a33",
                "sha256": "27b95e8401206ef7736831d6e3cde9dd26857834c0fc48447e4c15caa5e6b7f4"
            },
            "downloads": -1,
            "filename": "match_expression-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b0936d4cfb0adc9caa7a289205aa8a33",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 5196,
            "upload_time": "2025-09-03T02:52:47",
            "upload_time_iso_8601": "2025-09-03T02:52:47.966827Z",
            "url": "https://files.pythonhosted.org/packages/7e/d0/2f4f4d0c9c5ad5c63235816a7500b6f3844c271f98cecab30c07933fd9c3/match_expression-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "08af18d19aca9f4065c9da481280f81e8276e3a4325e2536f59fa219ed8d8d1d",
                "md5": "86e0d62dfe41c31ea081382ad972af0b",
                "sha256": "6bc0d2f320bcd4a646a7e3f57e89cdea4df5b282b61cdf6f74ad0d4f91c53e24"
            },
            "downloads": -1,
            "filename": "match_expression-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "86e0d62dfe41c31ea081382ad972af0b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 6141,
            "upload_time": "2025-09-03T02:52:49",
            "upload_time_iso_8601": "2025-09-03T02:52:49.175187Z",
            "url": "https://files.pythonhosted.org/packages/08/af/18d19aca9f4065c9da481280f81e8276e3a4325e2536f59fa219ed8d8d1d/match_expression-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-03 02:52:49",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "qodot",
    "github_project": "match-expression",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "match-expression"
}
        
Elapsed time: 1.80100s