text: from typing import List, Union
  
  from fuzzy_search.fuzzy_match import PhraseMatch, PhraseMatchInContext
  from fuzzy_search.fuzzy_phrase_searcher import FuzzyPhraseSearcher
  
  
  class FuzzyContextSearcher(FuzzyPhraseSearcher):
  
      def __init__(self, config: Union[dict, None] = None):
          super().__init__(config)
          self.context_size = 100
          if config is not None:
              self.configure_context(config)
  
      def configure_context(self, config: dict) -> None:
          """Configure the context searcher.
  
          :param config: a dictionary with configuration parameters to override the defaults
          :type config: dict
          """
          super().configure(config)
          if "context_size" in config:
              self.context_size = config["context_size"]
  
      def add_match_context(self, match: PhraseMatch, text: Union[str, dict], context_size: Union[None, int] = None,
                            prefix_size: Union[None, int] = None,
                            suffix_size: Union[None, int] = None) -> PhraseMatchInContext:
          """Add context to a given match and its corresponding text document.
  
          :param match: a phrase match object
          :type match: PhraseMatch
          :param text: the text that the match was taken from
          :type text: Union[str, dict]
          :param context_size: the size of the pre- and suffix window
          :type context_size: int
          :param prefix_size: size of the prefix context
          :type prefix_size: Union[None, int]
          :param suffix_size: size of the suffix context
          :type suffix_size: Union[None, int]
          :return: the phrase match object with context
          :rtype: PhraseMatchInContext
          """
          if context_size is None:
              context_size = self.context_size
          prefix_size = prefix_size if prefix_size is not None else context_size
          suffix_size = suffix_size if suffix_size is not None else context_size
          return PhraseMatchInContext(match, text, prefix_size=prefix_size, suffix_size=suffix_size)
  
      def find_matches(self, text: Union[str, dict],
                       use_word_boundaries: Union[None, bool] = None,
                       allow_overlapping_matches: bool = True,
                       include_variants: bool = None,
                       filter_distractors: bool = None,
                       prefix_size: Union[None, int] = None,
                       suffix_size: Union[None, int] = None,
                       skip_exact_matching: bool = None) -> List[PhraseMatchInContext]:
          """Find fuzzy matches for registered phrases and add context around match string. This extends
          the find_matches function of the FuzzyPhraseSearcher by adding local context to each match.
  
          :param text: the text (string or dictionary with 'text' property) to find fuzzy matching phrases in.
          :type text: Union[str, Dict[str, str]]
          :param use_word_boundaries: use word boundaries in determining match boundaries
          :type use_word_boundaries: bool
          :param allow_overlapping_matches: boolean flag for whether to allow matches to overlap in their text ranges
          :type allow_overlapping_matches: bool
          :param include_variants: boolean flag for whether to include phrase variants for finding matches
          :type include_variants: bool
          :param filter_distractors: boolean flag for whether to remove phrase matches that better match distractors
          :type filter_distractors: bool
          :param prefix_size: the size of the prefix context window
          :type prefix_size: Union[None, int]
          :param suffix_size: the size of the suffix context window
          :type suffix_size: Union[None, int]
          :param skip_exact_matching: boolean flag whether to skip the exact matching step
          :type skip_exact_matching: Union[None, bool]
          :return: a list of phrases matches with text surrounding the match string
          :rtype: PhraseMatchInContext
          """
          matches = super().find_matches(text, use_word_boundaries=use_word_boundaries,
                                         allow_overlapping_matches=allow_overlapping_matches,
                                         include_variants=include_variants, filter_distractors=filter_distractors,
                                         skip_exact_matching=skip_exact_matching)
          return [self.add_match_context(match, text, prefix_size=prefix_size,
                                         suffix_size=suffix_size) for match in matches]
  
      def find_matches_in_context(self, match_in_context: PhraseMatchInContext,
                                  use_word_boundaries: Union[None, bool] = None,
                                  include_variants: Union[None, bool] = None,
                                  filter_distractors: Union[None, bool] = None) -> List[PhraseMatch]:
          """Use a MatchInContext object to find other phrases in the context of that match.
  
          :param match_in_context: a match phrase with context from the text that the match was taken from
          :type match_in_context: PhraseMatchInContext
          :param use_word_boundaries: boolean whether to adjust match strings to word boundaries
          :type use_word_boundaries: bool
          :param include_variants: boolean whether to include variants of phrases in matching
          :type include_variants: bool
          :param filter_distractors: boolean whether to remove matches that are closer to distractors
          :type filter_distractors: bool
          :return: a list of match objects
          :rtype: List[PhraseMatch]
          """
          # override searcher configuration if a boolean is passed
          use_word_boundaries = self.use_word_boundaries if use_word_boundaries is None else use_word_boundaries
          include_variants = self.include_variants if include_variants is None else include_variants
          filter_distractors = self.filter_distractors if filter_distractors is None else filter_distractors
          # search the context of the original match
          context_matches = self.find_matches(match_in_context.context, use_word_boundaries=use_word_boundaries,
                                              include_variants=include_variants,
                                              filter_distractors=filter_distractors)
          # recalculate the match offset with respect to the original text
          for match in context_matches:
              match.offset += match_in_context.context_start
              match.end += match_in_context.context_start
          return context_matches
  
  
  if __name__ == "__main__":
      sample_text = "A'nthony van der Truyn en Adriaen Bosman, Makelaers tot Rotterdam, prefenteren," + \
                    "uyt de Hint te verkopen etfn curieufc Party opreckw ?al somfl'e Schalyen of Leyen."
  