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
Release history Release notifications | RSS feed
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
Hashes for awesome_pattern_matching-0.2.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | b1f7aca32bf55cdda77d9a7923b6766484ba96960742823c75ea99509051d218 |
|
MD5 | 0967bd3c905c711558a178d30bca8a57 |
|
BLAKE2b-256 | cfaa906520bd7f8882a0aa8cc1f5773a9f0242ff5fd2c728e2d3ff001fd7f64c |
Hashes for awesome_pattern_matching-0.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | ac63c13339a521a629a9d8f2f8e2e226a465dff9e8359c7d63fafb3cdb724096 |
|
MD5 | d1e215fdf5b935062d438a043f350457 |
|
BLAKE2b-256 | f76a92bb71c51a4268b65da601ab2a9a12f5693da8c7e6096ddaa8aa5868ad84 |