Skip to main content

Awesome Pattern Matching

Project description

Awesome Python Pattern Matching

  • Simple
  • Powerful
  • Extensible
  • Python 3.8+
  • Typed (IDE friendly)
from apm import *
from apm.patterns import Regex

record = {
    "ID": 9340,
    "First-Name": "Jane",
    "Last-Name": "Doe",
}

if result := match(record, {"First-Name": Capture(Regex("[A-Z][a-z]*"), name="name")}):
    print(result['name'])

Some Features

Demonstrated below: Junction of Patterns using &, Strict dictionary matching, Each.

records = [
    {
        "Foo": 1,
        "Bar": "Quux"
    },
    {
        "Foo": 2,
        "Bar": "Baz"
    }
]

assertTrue(
    match(records, Each(Strict({"Foo": InstanceOf(int), "Bar": InstanceOf(str) & Regex("[A-Z][a-z]+")}))))

records = [
    {
        "Foo": 1,
        "Bar": "Quux"
    },
    {
        "Foo": 2,
        "Bar": "Baz",
        "Strict": "Does not allow unknown keys"
    }
]

assertFalse(
    match(records, Each(Strict({"Foo": InstanceOf(int), "Bar": InstanceOf(str) & Regex("[A-Z][a-z]+")}))))

records = [
    {
        "Foo": 1,
        "Bar": "Quux"
    },
    {
        "Foo": 2,
        "Bar": "Baz",
        "No Problem": "When Not Strict"
    }
]

assertTrue(  # Note how this pattern is the same as above but without `Strict`
    match(records, Each({"Foo": InstanceOf(int), "Bar": InstanceOf(str) & Regex("[A-Z][a-z]+")})))

Very slim User Guide

Any value which occurs verbatim in a pattern is matched verbatim (int, str, list, ...), except Dictionaries ( anything which has an items() actually).

Thus:

some_very_complex_object = {
    "A": 1,
    "B": 2,
    "C": 3,
}
match(some_very_complex_object, {"C": 3})  # matches!

If you do not want unknown keys to be ignored, wrap the pattern in a Strict:

# does not match, only matches exactly `{"C": 3}`
match(some_very_complex_object, Strict({"C": 3}))

Lists (anything iterable which does not have an items() actually) are also compared as they are, i.e.:

ls = [1, 2, 3]
match(ls, [1, 2, 3])  # matches
match(ls, [1, 2])  # does not match

It is possible to match the remainder of a list though:

match(ls, [1, 2, Remaining(InstanceOf(int))])

And each item:

match(ls, Each(InstanceOf(int)))

Patterns can be joined using &, |, and ^:

match(ls, Each(InstanceOf(int) & Between(1, 3)))

Wild-card matches are supported using Ellipsis (...):

match(ls, [1, Remaining(..., at_least=2)])

The above example also showcases how Remaining can be made to match at_least n number of items (Each also has an at_least keyword argument).

Capture(pattern, name=<str>) (apm.*)

Captures a piece of the thing being matched by name.

if result := match([1, 2, 3, 4], [1, 2, Capture(Remaining(InstanceOf(int)), name='tail')]):
    print(result['tail'])  ## -> [3, 4]

Each(pattern [, at_least=] (apm.*)

Matches each item in an iterable.

match(range(1, 10), Each(Between(1, 9)))

OneOf(pattern1, pattern2, ...) (apm.*)

Matches against any of the provided patterns. Equivalent to p1 | p2 | p3 (but operator overloading does not work with values that do not inherit from Pattern)

match("quux", OneOf("bar", "baz", "quux"))

Extensible

New patterns can be added, just like the ones in apm.patterns.*. Simply extend the apm.Pattern class:

class Min(Pattern):
    def __init__(self, min):
        self.min = min

    def match(self, value, *, ctx: MatchContext, strict=False) -> MatchResult:
        return ctx.match_if(value >= self.min)

match(3, Min(1))  # matches
match(3, Min(5))  # does not match

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

awesome_pattern_matching-0.2.0.tar.gz (6.1 kB view details)

Uploaded Source

Built Distribution

File details

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

File metadata

  • Download URL: awesome_pattern_matching-0.2.0.tar.gz
  • Upload date:
  • Size: 6.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.3

File hashes

Hashes for awesome_pattern_matching-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b1f7aca32bf55cdda77d9a7923b6766484ba96960742823c75ea99509051d218
MD5 0967bd3c905c711558a178d30bca8a57
BLAKE2b-256 cfaa906520bd7f8882a0aa8cc1f5773a9f0242ff5fd2c728e2d3ff001fd7f64c

See more details on using hashes here.

Provenance

File details

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

File metadata

  • Download URL: awesome_pattern_matching-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 7.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.3

File hashes

Hashes for awesome_pattern_matching-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac63c13339a521a629a9d8f2f8e2e226a465dff9e8359c7d63fafb3cdb724096
MD5 d1e215fdf5b935062d438a043f350457
BLAKE2b-256 f76a92bb71c51a4268b65da601ab2a9a12f5693da8c7e6096ddaa8aa5868ad84

See more details on using hashes here.

Provenance

Supported by

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