Skip to content

Commit

Permalink
Merge branch 'release/0.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
aranega committed Apr 29, 2023
2 parents 75bbc2a + a2ba4d2 commit 858ec8c
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.4.0
current_version = 0.5.0
commit = True
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# CHANGELOG

## 0.5.0

### Features

* Add new syntax to describe patterns and matchers

### Fixes

* Fix issue with `or` matcher. The matcher was cutting some search branch way too early, resulting in incomplete behaviors in some cases.



## 0.4.0

### Features
Expand Down
136 changes: 73 additions & 63 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions iguala/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .helpers import is_not, match
from .matchers import as_matcher, cond, extended, regex, is_
from .matchers import as_matcher, cond, extended, is_, regex
from .paths import as_path

__ALL__ = ["match", "as_matcher", "as_path", "is_not", "cond", "regex", "extended"]
__version__ = "0.4.0"
__version__ = "0.5.0"
4 changes: 4 additions & 0 deletions iguala/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def __mod__(self, properties):
self.matcher.properties = properties
return self.matcher

def __getitem__(self, keys):
self.matcher.properties = keys if isinstance(keys, tuple) else [keys]
return self.matcher

def __matmul__(self, alias):
from .matchers import SaveNodeMatcher

Expand Down
20 changes: 13 additions & 7 deletions iguala/matchers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import MutableMapping
import itertools
from re import compile
from types import LambdaType

Expand Down Expand Up @@ -109,6 +110,9 @@ def match(self, obj):
def __or__(self, right):
return OrMatcher(self, as_matcher(right))

def __ror__(self, left):
return OrMatcher(as_matcher(left), self)

def save_as(self, alias):
return SaveNodeMatcher(alias, self)

Expand Down Expand Up @@ -197,17 +201,16 @@ def __init__(self, left, right):
self.right = right

def match_context(self, obj, context):
contexts = self.left.match_context(obj, context)
if any(c.is_match for c in contexts):
return contexts
return self.right.match_context(obj, context)
left_contexts = self.left.match_context(obj, context.copy())
right_contexts = self.right.match_context(obj, context.copy())
return [c for c in left_contexts + right_contexts if c.is_match]


class KeyValueMatcher(object):
def match_context(self, obj, context):
context.is_match = True
new_contexts = [context]
for path, matcher in self.properties.items():
for path, matcher in self.properties:
results = []
for context in new_contexts:
if matcher.is_collection_matcher:
Expand Down Expand Up @@ -251,9 +254,12 @@ def properties(self):
def properties(self, properties):
if properties is None:
self._properties = {}
return
elif isinstance(properties, dict):
props = [(as_path(k), as_matcher(v)) for k, v in properties.items()]
else:
props = {as_path(k): as_matcher(v) for k, v in properties.items()}
self._properties = props
props = [(as_path(sl.start), as_matcher(sl.stop)) for sl in properties]
self._properties = props


class DictMatcher(KeyValueMatcher, Matcher):
Expand Down
1 change: 1 addition & 0 deletions iguala/paths.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# from types import LambdaType
from functools import lru_cache

from .helpers import IdentitySet, flat

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

setup(
name='iguala',
version='0.4.0',
version='0.5.0',
description=("Non-linear pattern matching for Python's objects, or rexep-like for objects"),
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
Expand Down
34 changes: 34 additions & 0 deletions tests/data_for_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@ class BTest(ATest):
),
)

obj_test2 = ATest(
x=4,
y=8,
name="ATest name",
inner=InnerTest(name="foo", value=3),
inner_list=(
InnerTest(
name="foo",
value=4,
active=True,
children=[
InnerTest(
name="foo.foo",
value=8,
active=True,
children=[
InnerTest(
name="foo.foo.bar",
value=1,
children=[
InnerTest(name="foo.foo.bar.bar", value=1),
InnerTest(name="foo.foo.bar.baz", value=2, active=True),
],
),
InnerTest(name="foo.foo.baz", value=2),
],
)
],
),
InnerTest(name="bar", value=3, active=True),
InnerTest(name="foo", value=4),
),
)


dict_test = {
"x": 4,
Expand Down
65 changes: 63 additions & 2 deletions tests/test_object_matchers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
from iguala import match, as_matcher
from iguala import as_matcher, match

from .data_for_tests import obj_test


def test_lambda_matcher():
pattern = match(obj_test.__class__)[
"x":"@x",
"y" : as_matcher(lambda x: x * 2) @ "y",
]

result = pattern.match(obj_test)
assert result.is_match

bindings = result.contexts[0]
assert len(bindings) == 2
assert bindings["x"] == 4
assert bindings["y"] == 8

for z in bindings:
assert z in ("x", "y")

del bindings["x"]
assert len(bindings) == 1
assert "x" not in bindings
assert bindings["y"] == 8


def test_lambda_matcher2():
pattern = match(obj_test.__class__) % {
"x": "@x",
"y": as_matcher(lambda x: x * 2) @ "y",
Expand All @@ -25,6 +49,19 @@ def test_lambda_matcher():
assert bindings["y"] == 8


def test_lambda_matcher_uncomplete():
pattern = match(obj_test.__class__)["x":4, "y" : as_matcher(lambda x: x * 8)]

result = pattern.match(obj_test)
assert result.is_match

bindings = result.bindings[0]
assert "x" not in bindings

print(result)
assert bool(result) is True


def test_lambda_matcher_uncomplete():
pattern = match(obj_test.__class__) % {"x": 4, "y": as_matcher(lambda x: x * 8)}

Expand All @@ -39,6 +76,20 @@ def test_lambda_matcher_uncomplete():


def test_lambda_matcher_desorder():
pattern = match(obj_test.__class__)[
"y" : as_matcher(lambda x: x * 2) @ "y",
"x":"@x",
]

result = pattern.match(obj_test)
assert result.is_match

bindings = result.bindings[0]
assert bindings["x"] == 4
assert bindings["y"] == 8


def test_lambda_matcher_desorder2():
pattern = match(obj_test.__class__) % {
"y": as_matcher(lambda x: x * 2) @ "y",
"x": "@x",
Expand All @@ -52,7 +103,17 @@ def test_lambda_matcher_desorder():
assert bindings["y"] == 8


def test_lambda_matcher_desorder2():
def test_lambda_matcher_desorder3():
pattern = match(obj_test.__class__)[
"y" : as_matcher(lambda x: x * 5) @ "y",
"x":"@x",
]

result = pattern.match(obj_test)
assert result.is_match is False


def test_lambda_matcher_desorder4():
pattern = match(obj_test.__class__) % {
"y": as_matcher(lambda x: x * 5) @ "y",
"x": "@x",
Expand Down
32 changes: 32 additions & 0 deletions tests/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,38 @@ def test_itself():
assert result.is_match


def test_itself2():
path = as_path("x>y*")
matcher = match(ComposedPath)[
"paths" : [
match(DirectPath)["path":"x"],
match(NamedRecursivePath)["path" : match(DirectPath) % {"path": "y"}],
]
]
result = matcher.match(path)
assert result.is_match

path = as_path("x*>y*")
matcher = match(ComposedPath)[
"paths" : [
match(NamedRecursivePath)["path" : match(DirectPath) % {"path": "x"}],
match(NamedRecursivePath)["path" : match(DirectPath) % {"path": "y"}],
]
]
result = matcher.match(path)
assert result.is_match

path = as_path("x+")
matcher = match(ComposedPath)[
"paths" : [
match(DirectPath)["path":"x"],
match(NamedRecursivePath)["path" : match(DirectPath) % {"path": "x"}],
]
]
result = matcher.match(path)
assert result.is_match


@pytest.mark.parametrize(
"path, data, expected",
[
Expand Down
Loading

0 comments on commit 858ec8c

Please sign in to comment.