Skip to content

Commit

Permalink
Update/remove old Matcher syntax (explosion#11370)
Browse files Browse the repository at this point in the history
* Clean up old Matcher call style related stuff

In v2 Matcher.add was called with (key, on_match, *patterns). In v3 this
was changed to (key, patterns, *, on_match=None), but there were various
points where the old call syntax was documented or handled specially.
This removes all those.

The Matcher itself didn't need any code changes, as it just gives a
generic type error. However the PhraseMatcher required some changes
because it would automatically "fix" the old call style.

Surprisingly, the tokenizer was still using the old call style in one
place.

After these changes tests failed in two places:

1. one test for the "new" call style, including the "old" call style. I
   removed this test.
2. deserializing the PhraseMatcher fails because the input docs are a
   set.

I am not sure why 2 is happening - I guess it's a quirk of the
serialization format? - so for now I just convert the set to a list when
deserializing. The check that the input Docs are a List in the
PhraseMatcher is a new check, but makes it parallel with the other
Matchers, which seemed like the right thing to do.

* Add notes related to input docs / deserialization type

* Remove Typing import

* Remove old note about call style change

* Apply suggestions from code review

Co-authored-by: Adriane Boyd <adrianeboyd@gmail.com>

* Use separate method for setting internal doc representations

In addition to the title change, this changes the internal dict to be a
defaultdict, instead of a dict with frequent use of setdefault.

* Add _add_from_arrays for unpickling

* Cleanup around adding from arrays

This moves adding to internal structures into the private batch method,
and removes the single-add method.

This has one behavioral change for `add`, in that if something is wrong
with the list of input Docs (such as one of the items not being a Doc),
valid items before the invalid one will not be added. Also the callback
will not be updated if anything is invalid. This change should not be
significant.

This also adds a test to check failure when given a non-Doc.

* Update spacy/matcher/phrasematcher.pyx

Co-authored-by: Adriane Boyd <adrianeboyd@gmail.com>

Co-authored-by: Adriane Boyd <adrianeboyd@gmail.com>
  • Loading branch information
2 people authored and jikanter committed May 10, 2024
1 parent 0a7f40f commit 077bd7d
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 28 deletions.
35 changes: 10 additions & 25 deletions spacy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ class Errors(metaclass=ErrorsWithCodes):
"Current DocBin: {current}\nOther DocBin: {other}")
E169 = ("Can't find module: {module}")
E170 = ("Cannot apply transition {name}: invalid for the current state.")
E171 = ("{name}.add received invalid 'on_match' callback argument: expected "
E171 = ("{name}.add received invalid 'on_match' callback argument: expected "
"callable or None, but got: {arg_type}")
E175 = ("Can't remove rule for unknown match pattern ID: {key}")
Expand Down Expand Up @@ -750,6 +751,7 @@ class Errors(metaclass=ErrorsWithCodes):
"loaded nlp object, but got: {source}")
E947 = ("`Matcher.add` received invalid `greedy` argument: expected "
"a string value from {expected} but got: '{arg}'")
E948 = ("`{name}.add` received invalid 'patterns' argument: expected "
E948 = ("`{name}.add` received invalid 'patterns' argument: expected "
"a list, but got: {arg_type}")
E949 = ("Unable to align tokens for the predicted and reference docs. It "
Expand Down Expand Up @@ -980,33 +982,16 @@ class Errors(metaclass=ErrorsWithCodes):

# v4 error strings
E4000 = ("Expected a Doc as input, but got: '{type}'")
E4001 = ("Expected input to be one of the following types: ({expected_types}), "
"but got '{received_type}'")
E4002 = ("Pipe '{name}' requires a teacher pipe for distillation.")
E4003 = ("Training examples for distillation must have the exact same tokens in the "
"reference and predicted docs.")
E4004 = ("Backprop is not supported when is_train is not set.")
E4005 = ("EntityLinker_v1 is not supported in spaCy v4. Update your configuration.")
E4006 = ("Expected `entity_id` to be of type {exp_type}, but is of type {found_type}.")
E4007 = ("Span {var} {value} must be {op} Span {existing_var} "
"{existing_value}.")
E4008 = ("Span {pos}_char {value} does not correspond to a token {pos}.")
E4009 = ("The '{attr}' parameter should be 'None' or 'True', but found '{value}'.")
E4010 = ("Required lemmatizer table(s) {missing_tables} not found in "
"[initialize] or in registered lookups (spacy-lookups-data). An "
"example for how to load lemmatizer tables in [initialize]:\n\n"
"[initialize.components]\n\n"
"[initialize.components.{pipe_name}]\n\n"
"[initialize.components.{pipe_name}.lookups]\n"
'@misc = "spacy.LookupsDataLoaderFromURL.v1"\n'
"lang = ${{nlp.lang}}\n"
f'url = "{about.__lookups_url__}"\n'
"tables = {tables}\n"
"# or required tables only: tables = {required_tables}\n")
E4011 = ("Server error ({status_code}), couldn't fetch {url}")


RENAMED_LANGUAGE_CODES = {"xx": "mul", "is": "isl"}
# Deprecated model shortcuts, only used in errors and warnings
OLD_MODEL_SHORTCUTS = {
"en": "en_core_web_sm", "de": "de_core_news_sm", "es": "es_core_news_sm",
"pt": "pt_core_news_sm", "fr": "fr_core_news_sm", "it": "it_core_news_sm",
"nl": "nl_core_news_sm", "el": "el_core_news_sm", "nb": "nb_core_news_sm",
"lt": "lt_core_news_sm", "xx": "xx_ent_wiki_sm"
}


# fmt: on

Expand Down
15 changes: 12 additions & 3 deletions spacy/matcher/phrasematcher.pyx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# cython: infer_types=True, profile=True
from collections import defaultdict
from typing import List

from collections import defaultdict
from libc.stdint cimport uintptr_t
from preshed.maps cimport map_clear, map_get, map_init, map_iter, map_set
from preshed.maps cimport map_init, map_set, map_get, map_clear, map_iter

import warnings

Expand Down Expand Up @@ -45,6 +44,7 @@ cdef class PhraseMatcher:
self.vocab = vocab
self._callbacks = {}
self._docs = defaultdict(set)
self._docs = defaultdict(set)
self._validate = validate

self.mem = Pool()
Expand Down Expand Up @@ -160,22 +160,29 @@ cdef class PhraseMatcher:
del self._callbacks[key]
del self._docs[key]


def _add_from_arrays(self, key, specs, *, on_match=None):
"""Add a preprocessed list of specs, with an optional callback.
key (str): The match ID.
specs (List[List[int]]): A list of lists of hashes to match.
specs (List[List[int]]): A list of lists of hashes to match.
on_match (callable): Callback executed on match.
"""
"""
cdef MapStruct* current_node
cdef MapStruct* internal_node
cdef void* result
self._callbacks[key] = on_match
for spec in specs:
self._docs[key].add(tuple(spec))
self._callbacks[key] = on_match
for spec in specs:
self._docs[key].add(tuple(spec))
current_node = self.c_map
for token in spec:
for token in spec:
if token == self._terminal_hash:
warnings.warn(Warnings.W021)
Expand All @@ -195,6 +202,7 @@ cdef class PhraseMatcher:
result = internal_node
map_set(self.mem, <MapStruct*>result, self.vocab.strings[key], NULL)
def add(self, key, docs, *, on_match=None):
"""Add a match-rule to the phrase-matcher. A match-rule consists of: an ID
key, a list of one or more patterns, and (optionally) an on_match callback.
Expand Down Expand Up @@ -358,6 +366,7 @@ def unpickle_matcher(vocab, docs, callbacks, attr):
for key, specs in docs.items():
callback = callbacks.get(key, None)
matcher._add_from_arrays(key, specs, on_match=callback)
matcher._add_from_arrays(key, specs, on_match=callback)
return matcher
Expand Down

0 comments on commit 077bd7d

Please sign in to comment.