2019-02-12 17:47:26 +03:00
|
|
|
import pytest
|
2023-06-14 18:48:41 +03:00
|
|
|
|
2019-02-12 17:47:26 +03:00
|
|
|
from spacy.errors import MatchPatternError
|
2023-06-14 18:48:41 +03:00
|
|
|
from spacy.matcher import Matcher
|
2019-12-25 14:39:49 +03:00
|
|
|
from spacy.schemas import validate_token_pattern
|
2019-02-12 17:47:26 +03:00
|
|
|
|
2019-08-21 15:00:37 +03:00
|
|
|
# (pattern, num errors with validation, num errors identified with minimal
|
|
|
|
# checks)
|
|
|
|
TEST_PATTERNS = [
|
|
|
|
# Bad patterns flagged in all cases
|
|
|
|
([{"XX": "foo"}], 1, 1),
|
|
|
|
([{"IS_ALPHA": {"==": True}}, {"LIKE_NUM": None}], 2, 1),
|
|
|
|
([{"IS_PUNCT": True, "OP": "$"}], 1, 1),
|
|
|
|
([{"_": "foo"}], 1, 1),
|
|
|
|
('[{"TEXT": "foo"}, {"LOWER": "bar"}]', 1, 1),
|
2022-01-20 15:18:39 +03:00
|
|
|
([{"ENT_IOB": "foo"}], 1, 1),
|
2019-08-21 15:00:37 +03:00
|
|
|
([1, 2, 3], 3, 1),
|
2022-06-30 12:01:58 +03:00
|
|
|
([{"TEXT": "foo", "OP": "{,}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{,4}4"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{a,3}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{a}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{,a}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{1,2,3}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{1, 3}"}], 1, 1),
|
|
|
|
([{"TEXT": "foo", "OP": "{-2}"}], 1, 1),
|
2019-08-21 15:00:37 +03:00
|
|
|
# Bad patterns flagged outside of Matcher
|
2019-12-25 14:39:49 +03:00
|
|
|
([{"_": {"foo": "bar", "baz": {"IN": "foo"}}}], 2, 0), # prev: (1, 0)
|
2019-08-21 15:00:37 +03:00
|
|
|
# Bad patterns not flagged with minimal checks
|
2019-10-16 14:40:18 +03:00
|
|
|
([{"LENGTH": "2", "TEXT": 2}, {"LOWER": "test"}], 2, 0),
|
2019-12-25 14:39:49 +03:00
|
|
|
([{"LENGTH": {"IN": [1, 2, "3"]}}, {"POS": {"IN": "VERB"}}], 4, 0), # prev: (2, 0)
|
|
|
|
([{"LENGTH": {"VALUE": 5}}], 2, 0), # prev: (1, 0)
|
|
|
|
([{"TEXT": {"VALUE": "foo"}}], 2, 0), # prev: (1, 0)
|
2019-10-16 14:40:18 +03:00
|
|
|
([{"IS_DIGIT": -1}], 1, 0),
|
|
|
|
([{"ORTH": -1}], 1, 0),
|
2021-11-24 12:37:10 +03:00
|
|
|
([{"ENT_ID": -1}], 1, 0),
|
|
|
|
([{"ENT_KB_ID": -1}], 1, 0),
|
2019-08-21 15:00:37 +03:00
|
|
|
# Good patterns
|
|
|
|
([{"TEXT": "foo"}, {"LOWER": "bar"}], 0, 0),
|
|
|
|
([{"LEMMA": {"IN": ["love", "like"]}}, {"POS": "DET", "OP": "?"}], 0, 0),
|
|
|
|
([{"LIKE_NUM": True, "LENGTH": {">=": 5}}], 0, 0),
|
2019-10-16 14:40:18 +03:00
|
|
|
([{"LENGTH": 2}], 0, 0),
|
2019-08-21 15:00:37 +03:00
|
|
|
([{"LOWER": {"REGEX": "^X", "NOT_IN": ["XXX", "XY"]}}], 0, 0),
|
|
|
|
([{"NORM": "a"}, {"POS": {"IN": ["NOUN"]}}], 0, 0),
|
|
|
|
([{"_": {"foo": {"NOT_IN": ["bar", "baz"]}, "a": 5, "b": {">": 10}}}], 0, 0),
|
2019-12-25 14:39:49 +03:00
|
|
|
([{"orth": "foo"}], 0, 0), # prev: xfail
|
2020-03-03 14:22:39 +03:00
|
|
|
([{"IS_SENT_START": True}], 0, 0),
|
|
|
|
([{"SENT_START": True}], 0, 0),
|
2021-11-24 12:37:10 +03:00
|
|
|
([{"ENT_ID": "STRING"}], 0, 0),
|
|
|
|
([{"ENT_KB_ID": "STRING"}], 0, 0),
|
2022-06-30 12:01:58 +03:00
|
|
|
([{"TEXT": "ha", "OP": "{3}"}], 0, 0),
|
2019-08-21 15:00:37 +03:00
|
|
|
]
|
|
|
|
|
2019-02-12 17:47:26 +03:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-07-18 11:00:07 +03:00
|
|
|
"pattern",
|
|
|
|
[[{"XX": "y"}], [{"LENGTH": "2"}], [{"TEXT": {"IN": 5}}], [{"text": {"in": 6}}]],
|
2019-02-12 17:47:26 +03:00
|
|
|
)
|
|
|
|
def test_matcher_pattern_validation(en_vocab, pattern):
|
|
|
|
matcher = Matcher(en_vocab, validate=True)
|
|
|
|
with pytest.raises(MatchPatternError):
|
2019-10-25 23:21:08 +03:00
|
|
|
matcher.add("TEST", [pattern])
|
2019-02-12 17:47:26 +03:00
|
|
|
|
|
|
|
|
2019-08-21 15:00:37 +03:00
|
|
|
@pytest.mark.parametrize("pattern,n_errors,_", TEST_PATTERNS)
|
2019-12-25 14:39:49 +03:00
|
|
|
def test_pattern_validation(pattern, n_errors, _):
|
|
|
|
errors = validate_token_pattern(pattern)
|
2019-08-21 15:00:37 +03:00
|
|
|
assert len(errors) == n_errors
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("pattern,n_errors,n_min_errors", TEST_PATTERNS)
|
|
|
|
def test_minimal_pattern_validation(en_vocab, pattern, n_errors, n_min_errors):
|
|
|
|
matcher = Matcher(en_vocab)
|
|
|
|
if n_min_errors > 0:
|
|
|
|
with pytest.raises(ValueError):
|
2019-10-25 23:21:08 +03:00
|
|
|
matcher.add("TEST", [pattern])
|
2019-08-21 15:00:37 +03:00
|
|
|
elif n_errors == 0:
|
2019-10-25 23:21:08 +03:00
|
|
|
matcher.add("TEST", [pattern])
|
2020-08-05 15:56:14 +03:00
|
|
|
|
|
|
|
|
2020-09-04 15:05:55 +03:00
|
|
|
def test_pattern_errors(en_vocab):
|
2020-08-05 15:56:14 +03:00
|
|
|
matcher = Matcher(en_vocab)
|
|
|
|
# normalize "regex" to upper like "text"
|
|
|
|
matcher.add("TEST1", [[{"text": {"regex": "regex"}}]])
|
2020-09-04 15:05:55 +03:00
|
|
|
# error if subpattern attribute isn't recognized and processed
|
|
|
|
with pytest.raises(MatchPatternError):
|
2020-08-05 15:56:14 +03:00
|
|
|
matcher.add("TEST2", [[{"TEXT": {"XX": "xx"}}]])
|