Coverage for presidio_analyzer / predefined_recognizers / nlp_engine_recognizers / spacy_recognizer.py: 95%
40 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
1import logging
2import warnings
3from typing import List, Optional, Set, Tuple
5from presidio_analyzer import (
6 AnalysisExplanation,
7 LocalRecognizer,
8 RecognizerResult,
9)
11logger = logging.getLogger("presidio-analyzer")
14class SpacyRecognizer(LocalRecognizer):
15 """
16 Recognize PII entities using a spaCy NLP model.
18 Since the spaCy pipeline is ran by the AnalyzerEngine/SpacyNlpEngine,
19 this recognizer only extracts the entities from the NlpArtifacts
20 and returns them.
22 """
24 ENTITIES = ["DATE_TIME", "NRP", "LOCATION", "PERSON", "ORGANIZATION"]
26 DEFAULT_EXPLANATION = "Identified as {} by Spacy's Named Entity Recognition"
28 # deprecated, use MODEL_TO_PRESIDIO_MAPPING in NerModelConfiguration instead
29 CHECK_LABEL_GROUPS = [
30 ({"LOCATION"}, {"GPE", "LOC"}),
31 ({"PERSON", "PER"}, {"PERSON", "PER"}),
32 ({"DATE_TIME"}, {"DATE", "TIME"}),
33 ({"NRP"}, {"NORP"}),
34 ({"ORGANIZATION"}, {"ORG"}),
35 ]
37 def __init__(
38 self,
39 supported_language: str = "en",
40 supported_entities: Optional[List[str]] = None,
41 ner_strength: float = 0.85,
42 default_explanation: Optional[str] = None,
43 check_label_groups: Optional[List[Tuple[Set, Set]]] = None,
44 context: Optional[List[str]] = None,
45 name: Optional[str] = None,
46 ):
47 """Initialize the SpaCy recognizer.
49 :param supported_language: Language this recognizer supports
50 :param supported_entities: The entities this recognizer can detect
51 :param ner_strength: Default confidence for NER prediction
52 :param check_label_groups: (DEPRECATED) Tuple containing Presidio entity names
53 :param default_explanation: Default explanation for the results when using return_decision_process=True
54 """ # noqa: E501
56 self.ner_strength = ner_strength
57 if check_label_groups:
58 warnings.warn(
59 "check_label_groups is deprecated and isn't used;"
60 "entities are mapped in NerModelConfiguration",
61 DeprecationWarning,
62 2,
63 )
65 self.default_explanation = (
66 default_explanation if default_explanation else self.DEFAULT_EXPLANATION
67 )
68 supported_entities = supported_entities if supported_entities else self.ENTITIES
69 super().__init__(
70 supported_entities=supported_entities,
71 supported_language=supported_language,
72 context=context,
73 name=name,
74 )
76 def load(self) -> None: # noqa: D102
77 # no need to load anything as the analyze method already receives
78 # preprocessed nlp artifacts
79 pass
81 def build_explanation(
82 self, original_score: float, explanation: str
83 ) -> AnalysisExplanation:
84 """
85 Create explanation for why this result was detected.
87 :param original_score: Score given by this recognizer
88 :param explanation: Explanation string
89 :return:
90 """
91 explanation = AnalysisExplanation(
92 recognizer=self.name,
93 original_score=original_score,
94 textual_explanation=explanation,
95 )
96 return explanation
98 def analyze(self, text: str, entities, nlp_artifacts=None): # noqa: D102
99 results = []
100 if not nlp_artifacts:
101 logger.warning("Skipping SpaCy, nlp artifacts not provided...")
102 return results
104 ner_entities = nlp_artifacts.entities
105 ner_scores = nlp_artifacts.scores
107 for ner_entity, ner_score in zip(ner_entities, ner_scores):
108 if (
109 ner_entity.label_ not in entities
110 or ner_entity.label_ not in self.supported_entities
111 ):
112 logger.debug(
113 f"Skipping entity {ner_entity.label_} "
114 f"as it is not in the supported entities list"
115 )
116 continue
118 textual_explanation = self.DEFAULT_EXPLANATION.format(ner_entity.label_)
119 explanation = self.build_explanation(ner_score, textual_explanation)
120 spacy_result = RecognizerResult(
121 entity_type=ner_entity.label_,
122 start=ner_entity.start_char,
123 end=ner_entity.end_char,
124 score=ner_score,
125 analysis_explanation=explanation,
126 recognition_metadata={
127 RecognizerResult.RECOGNIZER_NAME_KEY: self.name,
128 RecognizerResult.RECOGNIZER_IDENTIFIER_KEY: self.id,
129 },
130 )
131 results.append(spacy_result)
133 return results
135 @staticmethod
136 def __check_label(
137 entity: str, label: str, check_label_groups: Tuple[Set, Set]
138 ) -> bool:
139 raise DeprecationWarning("__check_label is deprecated")